diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/doc/README.hier postgresql-7.2.3-evg/doc/README.hier *** postgresql-7.2.3/doc/README.hier Thu Jan 1 04:00:00 1970 --- postgresql-7.2.3-evg/doc/README.hier Fri Nov 22 14:41:35 2002 *************** *** 0 **** --- 1,131 ---- + + WHAT'S THIS + + This is a patch which allows PG to make hierarchical queries a la Oracle do. + Requires re-initdb (see in misc). + + Copyright (c) Evgen Potemkin 2002, evgent@terminal.ru + Distributed under same terms as PostgreSQL itself. + + INSTALLATION + + tar xzf postgresql-7.2.3.tar.gz + patch -p0 < hier.diff + cd postgresql-7.2.3/ + ./configure + ... so on + + SYNOPSIS + + >create table data (id int4, pnt int4, data varchar(20)); + + data: + +----+-----+-------------+ + | id | pnt | data | + +----+-----+-------------+ + | 0 | 0 | root | + | 1 | 0 | (1 leaf l1) | + | 2 | 0 | (2 leaf l1) | + | 3 | 0 | (3 leaf l1) | + | 4 | 1 | (11 leaf l2)| + | 5 | 1 | (12 leaf l2)| + | 6 | 1 | (13 leaf l2)| + | 7 | 6 | (31 leaf l2)| + | 8 | 6 | (32 leaf l2)| + +----+-----+-------------+ + + > SELECT * FROM data CONNECT BY id PRIOR pnt START WITH id=0; + + output: + + id | pnt | data | _level_ + ----+-----+-------------+--------- + 0 | 0 | root | 1 + 1 | 0 | (1 leaf l1) | 2 + 4 | 1 | (11 leaf l2)| 3 + 5 | 1 | (12 leaf l2)| 3 + 6 | 1 | (13 leaf l2)| 3 + 2 | 0 | (2 leaf l1) | 2 + 3 | 0 | (3 leaf l1) | 2 + 7 | 6 | (31 leaf l2)| 3 + 8 | 6 | (32 leaf l2)| 3 + + DESCRIPTION + + Hierarchical query. + + Lets tree is like: + (root) + / | \ + / | \ + (1 leaf l1) (2 leaf l1) (3 leaf l1) + / | \ | \ + / | \ | \ + (11 leaf l2) (12 leaf l2) (13 leaf l2) (31 leaf l2) (32 leaf l2) + + so lets define hierarchical query as query which return us a set like: + +------------+ + |root | + |(1 leaf l1) | + |(11 leaf l2)| + |(12 leaf l2)| + |(13 leaf l2)| + |(2 leaf l1) | + |(3 leaf l1) | + |(31 leaf l2)| + |(32 leaf l2)| + +------------+ + + i.e. first is root,then first child of root, + then first child of first child, if any, or second child otherwise, ... so on. + + Reason. + + Often hierarchical queries are made by adding special field which stores a position + of a row in the tree, and then make "SELECT ... ORDER BY 'hier_position_field'" + over the table. disability of such approach is that we can't sort leaves in some + custom order. + + This patch is based on internal PG's query processing and havn't such disadvantage. + + Syntax. + + SELECT ... FROM ... [ WHERE condition ] CONNECT BY c_expr PRIOR c_expr START WITH expr + [ HAVING condition [, ...]] [ LIMIT ... ] [ OFFSET ... ] + + ... means the same as usual SELECT + c_expr is a column name + expr is start expression + + WHERE condition + only rows which pass condition will be used + + CONNECT BY id PRIOR pnt + rows will be connected by parent 'id' and child 'pnt' columns. + i.e. if in some row (A) column 'pnt' is equal to 'id' column of other row (B) + means that row A is a child of B. + + START WITH expr + rows which not fail qual by expr will be treaten as root rows and other will + be connected to them. + + HAVING condition + used to qual rows after building hierarchy. only which pass will be returned. + + LIMIT, OFFSET + the same as usual + + Subqueries, views are supported. + + Results. + + query return sets like discribed in synopsis. '_level_' is additional column which + added only in case of hierarchical query. + it means deep of row in the tree, starts from 1. + + Misc. + + Patch is requiers initdb, because it's add some fields to internal view + representation (in nodes/outfuncs.c). so DB created with old PG will not work. + + diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/commands/explain.c postgresql-7.2.3-evg/src/backend/commands/explain.c *** postgresql-7.2.3/src/backend/commands/explain.c Wed Feb 27 03:48:38 2002 --- postgresql-7.2.3-evg/src/backend/commands/explain.c Fri Nov 22 14:48:23 2002 *************** *** 233,238 **** --- 233,241 ---- case T_Sort: pname = "Sort"; break; + case T_Conn: + pname = "Hier"; + break; case T_Group: pname = "Group"; break; diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/executor/Makefile postgresql-7.2.3-evg/src/backend/executor/Makefile *** postgresql-7.2.3/src/backend/executor/Makefile Tue Sep 18 06:59:06 2001 --- postgresql-7.2.3-evg/src/backend/executor/Makefile Fri Nov 22 14:48:23 2002 *************** *** 18,24 **** nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \ nodeNestloop.o nodeResult.o nodeSeqscan.o nodeSetOp.o nodeSort.o \ nodeUnique.o nodeLimit.o nodeGroup.o nodeSubplan.o \ ! nodeSubqueryscan.o nodeTidscan.o spi.o all: SUBSYS.o --- 18,24 ---- nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \ nodeNestloop.o nodeResult.o nodeSeqscan.o nodeSetOp.o nodeSort.o \ nodeUnique.o nodeLimit.o nodeGroup.o nodeSubplan.o \ ! nodeSubqueryscan.o nodeTidscan.o spi.o nodeConn.o all: SUBSYS.o diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/executor/execAmi.c postgresql-7.2.3-evg/src/backend/executor/execAmi.c *** postgresql-7.2.3/src/backend/executor/execAmi.c Thu Oct 25 10:49:27 2001 --- postgresql-7.2.3-evg/src/backend/executor/execAmi.c Fri Nov 22 14:48:23 2002 *************** *** 32,37 **** --- 32,38 ---- #include "executor/instrument.h" #include "executor/nodeAgg.h" #include "executor/nodeAppend.h" + #include "executor/nodeConn.h" #include "executor/nodeGroup.h" #include "executor/nodeGroup.h" #include "executor/nodeHash.h" *************** *** 353,358 **** --- 354,363 ---- ExecReScanSort((Sort *) node, exprCtxt, parent); break; + case T_Conn: + ExecReScanConn((Conn *) node, exprCtxt, parent); + break; + case T_MergeJoin: ExecReScanMergeJoin((MergeJoin *) node, exprCtxt, parent); break; *************** *** 425,430 **** --- 430,439 ---- ExecSortMarkPos((Sort *) node); break; + case T_Conn: + ExecConnMarkPos((Conn *) node); + break; + case T_TidScan: ExecTidMarkPos((TidScan *) node); break; *************** *** 465,470 **** --- 474,483 ---- case T_Sort: ExecSortRestrPos((Sort *) node); + break; + + case T_Conn: + ExecConnRestrPos((Conn *) node); break; default: diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/executor/execProcnode.c postgresql-7.2.3-evg/src/backend/executor/execProcnode.c *** postgresql-7.2.3/src/backend/executor/execProcnode.c Thu Oct 25 10:49:27 2001 --- postgresql-7.2.3-evg/src/backend/executor/execProcnode.c Fri Nov 22 14:48:23 2002 *************** *** 81,86 **** --- 81,87 ---- #include "executor/instrument.h" #include "executor/nodeAgg.h" #include "executor/nodeAppend.h" + #include "executor/nodeConn.h" #include "executor/nodeGroup.h" #include "executor/nodeHash.h" #include "executor/nodeHashjoin.h" *************** *** 198,203 **** --- 199,208 ---- result = ExecInitSort((Sort *) node, estate, parent); break; + case T_Conn: + result = ExecInitConn((Conn *) node, estate, parent); + break; + case T_Unique: result = ExecInitUnique((Unique *) node, estate, parent); break; *************** *** 327,332 **** --- 332,341 ---- result = ExecSort((Sort *) node); break; + case T_Conn: + result = ExecConn((Conn *) node); + break; + case T_Unique: result = ExecUnique((Unique *) node); break; *************** *** 416,421 **** --- 425,433 ---- case T_Sort: return ExecCountSlotsSort((Sort *) node); + case T_Conn: + return ExecCountSlotsConn((Conn *) node); + case T_Unique: return ExecCountSlotsUnique((Unique *) node); *************** *** 533,538 **** --- 545,554 ---- ExecEndSort((Sort *) node); break; + case T_Conn: + ExecEndConn((Conn *) node); + break; + case T_Unique: ExecEndUnique((Unique *) node); break; *************** *** 595,600 **** --- 611,624 ---- case T_SeqScan: { CommonScanState *scanstate = ((SeqScan *) node)->scanstate; + + slot = scanstate->cstate.cs_ResultTupleSlot; + } + break; + + case T_Conn: + { + CommonScanState *scanstate = ((Conn *) node)->connstate; slot = scanstate->cstate.cs_ResultTupleSlot; } diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/executor/nodeConn.c postgresql-7.2.3-evg/src/backend/executor/nodeConn.c *** postgresql-7.2.3/src/backend/executor/nodeConn.c Thu Jan 1 04:00:00 1970 --- postgresql-7.2.3-evg/src/backend/executor/nodeConn.c Fri Nov 22 14:48:23 2002 *************** *** 0 **** --- 1,339 ---- + /*------------------------------------------------------------------------- * + * 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(Conn *node) + { + EState *estate; + TupleTableSlot *slot; + HeapTuple heapTuple; + Plan *outerNode; + ConnectState *connstate; + ScanDirection dir; + Tupleconnstate *tupleconnstate; + bool should_free; + List *squal,*chqual; + ExprContext *econtext; + int head; + bool ok; + + /* + * get state info from node + */ + + estate = node->plan.state; + dir = estate->es_direction; + connstate=node->connstate; + tupleconnstate = (Tupleconnstate *) connstate->tupleconnstate; + + if (!connstate->conn_Done) + { + TupleDesc tupDesc; + + 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 = outerPlan((Plan *) node); + tupDesc = ExecGetTupType(outerNode); + + tupleconnstate = tupleconn_begin_heap(SortMem,tupDesc, + node->connOp,node->parent,node->child); + connstate->tupleconnstate = (void *) tupleconnstate; + + + /* + * Scan the subplan and feed all the tuples to tupleconn. + */ + squal=node->startQual; + chqual=node->childQual; + + econtext = connstate->csstate.cstate.cs_ExprContext; + for (;;) + { + ResetExprContext(econtext); + + slot = ExecProcNode(outerNode, (Plan *) node); + + if (TupIsNull(slot)) + break; + econtext->ecxt_scantuple = slot; + /* check if tuple is a head of tree */ + head = ExecQual(squal, econtext, false); + if(node->childQual){ + ResetExprContext(econtext); + /* if child qual exist and tuple don't conforms to it then skip it */ + if(ExecQual(chqual,econtext,false)) + tupleconn_puttuple(tupleconnstate, (void *) slot->val,head); + }else + tupleconn_puttuple(tupleconnstate, (void *) slot->val,head); + } + + ExecAssignResultType(&connstate->csstate.cstate, tupDesc, false); + + tupleconn_donestoring(tupleconnstate); + /* connect them */ + tupleconn_performconn(tupleconnstate); + /* + * finally set the connected flag to true + */ + connstate->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 = ((Plan *)node)->qual;//node->havingQual; + ok=0; + econtext = connstate->csstate.cstate.cs_ExprContext; + while(!ok){ + if(squal) ResetExprContext(econtext); + heapTuple = tupleconn_getheaptuple(tupleconnstate, + &should_free); + slot = connstate->csstate.cstate.cs_ResultTupleSlot; + ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free); + if(!squal) break; /* don't qual */ + if(!heapTuple) break; /* no tuples */ + econtext->ecxt_scantuple = slot; + ok=ExecQual(squal, econtext, false); + } + return slot; + } + + /* ---------------------------------------------------------------- + * ExecInitConn + * + * Creates the run-time state information for the conn node + * produced by the planner and initailizes its outer subtree. + * ---------------------------------------------------------------- + */ + bool + ExecInitConn(Conn *node, EState *estate, Plan *parent) + { + ConnectState *connstate; + Plan *outerPlan; + + SO1_printf("ExecInitConn: %s\n", + "initializing conn node"); + /* + * assign the node's execution state + */ + node->plan.state = estate; + + /* + * create state structure + */ + connstate = makeNode(ConnectState); + connstate->conn_Done = false; + connstate->tupleconnstate = NULL; + + node->connstate=connstate; + /* + * Miscellaneous initialization + */ + ExecAssignExprContext(estate, &connstate->csstate.cstate); + #define CONN_NSLOTS 2 + + /* + * tuple table initialization + */ + ExecInitResultTupleSlot(estate, &connstate->csstate.cstate); + ExecInitScanTupleSlot(estate, &connstate->csstate); + /* + * initializes child nodes + */ + outerPlan = outerPlan((Plan *) node); + ExecInitNode(outerPlan, estate, (Plan *) node); + + /* + * initialize tuple type. no need to initialize projection info + * because this node doesn't do projections. + */ + ExecAssignResultTypeFromOuterPlan((Plan *) node, &connstate->csstate.cstate); + ExecAssignScanTypeFromOuterPlan((Plan *) node, &connstate->csstate); + connstate->csstate.cstate.cs_ProjInfo = NULL; + + SO1_printf("ExecInitConn: %s\n", + "conn node initialized"); + + return TRUE; + } + + int + ExecCountSlotsConn(Conn *node) + { + return ExecCountSlotsNode(outerPlan((Plan *) node)) + + ExecCountSlotsNode(innerPlan((Plan *) node))+CONN_NSLOTS; + } + + /* ---------------------------------------------------------------- + * ExecEndConn(node) + * ---------------------------------------------------------------- + */ + void + ExecEndConn(Conn *node) + { + Plan *outerPlan; + ConnectState *connstate; + + /* + * get info from the conn state + */ + SO1_printf("ExecEndConn: %s\n", + "shutting down conn node"); + connstate=node->connstate; + + /* + * shut down the subplan + */ + outerPlan = outerPlan((Plan *) node); + ExecEndNode(outerPlan, (Plan *) node); + + /* + * clean out the tuple table + */ + ExecClearTuple(connstate->csstate.cstate.cs_ResultTupleSlot); + ExecClearTuple(connstate->csstate.css_ScanTupleSlot); + + /* + * Release tupleconn resources + */ + if (connstate->tupleconnstate != NULL) + tupleconn_end((Tupleconnstate *) connstate->tupleconnstate); + connstate->tupleconnstate = NULL; + + pfree(connstate); + node->connstate = NULL; + + SO1_printf("ExecEndConn: %s\n", + "conn node shutdown"); + } + + /* ---------------------------------------------------------------- + * ExecConnMarkPos + * + * Calls tupleconn to save the current position in the processed file. + * ---------------------------------------------------------------- + */ + void + ExecConnMarkPos(Conn *node) + { + ConnectState *connstate = node->connstate; + + /* + * if we haven't processed yet, just return + */ + if (!connstate->conn_Done) + return; + + tupleconn_markpos((Tupleconnstate *) connstate->tupleconnstate); + } + + /* ---------------------------------------------------------------- + * ExecConnRestrPos + * + * Calls tupleconn to restore the last saved processed file position. + * ---------------------------------------------------------------- + */ + void + ExecConnRestrPos(Conn *node) + { + ConnectState *connstate = node->connstate; + + /* + * if we haven't processed yet, just return. + */ + if (!connstate->conn_Done) + return; + + /* + * restore the scan to the previously marked position + */ + tupleconn_restorepos((Tupleconnstate *) connstate->tupleconnstate); + } + + void + ExecReScanConn(Conn *node, ExprContext *exprCtxt, Plan *parent) + { + ConnectState *connstate = node->connstate; + + /* + * 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 (!connstate->conn_Done) + return; + + ExecClearTuple(node->connstate->csstate.cstate.cs_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 (((Plan *) node)->lefttree->chgParam != NULL) + { + connstate->conn_Done = false; + tupleconn_end((Tupleconnstate *) connstate->tupleconnstate); + connstate->tupleconnstate = NULL; + } + else + tupleconn_rescan((Tupleconnstate *) connstate->tupleconnstate); + + /* + * if chgParam of subnode is not null then plan will be re-scanned by + * first ExecProcNode. + */ + if (((Plan *) node)->lefttree->chgParam == NULL) + ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node); + } diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/nodes/copyfuncs.c postgresql-7.2.3-evg/src/backend/nodes/copyfuncs.c *** postgresql-7.2.3/src/backend/nodes/copyfuncs.c Wed Feb 27 03:48:41 2002 --- postgresql-7.2.3-evg/src/backend/nodes/copyfuncs.c Fri Nov 22 14:48:23 2002 *************** *** 449,454 **** --- 449,477 ---- /* ---------------- + * _copyConn + * ---------------- + */ + static Conn * + _copyConn(Conn *from) + { + Conn *newnode = makeNode(Conn); + /* + * copy node superclass fields + */ + CopyPlanFields((Plan *) from, (Plan *) newnode); + + Node_Copy(from,newnode,startQual); + Node_Copy(from,newnode,childQual); + + newnode->parent = from->parent; + newnode->child = from->child; + newnode->connOp = newnode->connOp; + + return newnode; + } + + /* ---------------- * _copySort * ---------------- */ *************** *** 729,734 **** --- 752,759 ---- newnode->varnoold = from->varnoold; newnode->varoattno = from->varoattno; + newnode->varfake = from->varfake; + return newnode; } *************** *** 1520,1525 **** --- 1545,1565 ---- return newnode; } + static HierClause * + _copyHierClause(HierClause *from) + { + HierClause *newnode = makeNode(HierClause); + + Node_Copy(from,newnode,startQual); + Node_Copy(from,newnode,childQual); + + newnode->parent = from->parent; + newnode->child = from->child; + newnode->connOp = from->connOp; + + return newnode; + } + static SortClause * _copySortClause(SortClause *from) { *************** *** 1772,1777 **** --- 1812,1819 ---- Node_Copy(from, newnode, limitOffset); Node_Copy(from, newnode, limitCount); + Node_Copy(from, newnode, hierClause); + Node_Copy(from, newnode, setOperations); newnode->resultRelations = listCopy(from->resultRelations); *************** *** 2971,2976 **** --- 3013,3024 ---- break; case T_PrivGrantee: retval = _copyPrivGrantee(from); + break; + case T_Conn: + retval = _copyConn(from); + break; + case T_HierClause: + retval = _copyHierClause(from); break; default: diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/nodes/equalfuncs.c postgresql-7.2.3-evg/src/backend/nodes/equalfuncs.c *** postgresql-7.2.3/src/backend/nodes/equalfuncs.c Wed Feb 27 03:48:42 2002 --- postgresql-7.2.3-evg/src/backend/nodes/equalfuncs.c Fri Nov 22 14:48:23 2002 *************** *** 119,124 **** --- 119,126 ---- return false; if (a->varoattno != b->varoattno) return false; + if (a->varfake != b->varfake) + return false; return true; } diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/nodes/outfuncs.c postgresql-7.2.3-evg/src/backend/nodes/outfuncs.c *** postgresql-7.2.3/src/backend/nodes/outfuncs.c Thu Oct 25 19:08:11 2001 --- postgresql-7.2.3-evg/src/backend/nodes/outfuncs.c Fri Nov 22 14:48:23 2002 *************** *** 290,295 **** --- 290,298 ---- appendStringInfo(str, " :limitCount "); _outNode(str, node->limitCount); + + appendStringInfo(str, " :hierClause "); + _outNode(str, node->hierClause); appendStringInfo(str, " :setOperations "); _outNode(str, node->setOperations); *************** *** 299,310 **** --- 302,327 ---- } static void + _outHierClause(StringInfo str, HierClause *node) + { + appendStringInfo(str, " HIERCLAUSE :startQual "); + _outNode(str, node->startQual); + + appendStringInfo(str, " :childQual "); + _outNode(str, node->childQual); + + appendStringInfo(str, " :parent %u :child %u :connOp %u ", + node->parent,node->child,node->connOp); + } + + static void _outSortClause(StringInfo str, SortClause *node) { appendStringInfo(str, " SORTCLAUSE :tleSortGroupRef %u :sortop %u ", node->tleSortGroupRef, node->sortop); } + static void _outGroupClause(StringInfo str, GroupClause *node) { *************** *** 574,579 **** --- 591,611 ---- appendStringInfo(str, " :keycount %d ", node->keycount); } + /* + * Conn is a subclass of Plan + */ + static void + _outConn(StringInfo str, Conn *node){ + appendStringInfo(str, " CONN "); + _outPlanInfo(str, (Plan *) node); + appendStringInfo(str, " :startQual "); + _outNode(str, node->startQual); + appendStringInfo(str, " :childQual "); + _outNode(str, node->childQual); + appendStringInfo(str, " :parent %u :child %u :connop %u ", + node->parent, node->child,node->connOp); + } + static void _outAgg(StringInfo str, Agg *node) { *************** *** 747,756 **** node->vartype, node->vartypmod); ! appendStringInfo(str, " :varlevelsup %u :varnoold %u :varoattno %d", node->varlevelsup, node->varnoold, node->varoattno); } /* --- 779,792 ---- node->vartype, node->vartypmod); ! appendStringInfo(str, " :varlevelsup %u :varnoold %u :varoattno %d ", node->varlevelsup, node->varnoold, node->varoattno); + + appendStringInfo(str, " :varfake %u ", + node->varfake); + } /* *************** *** 1690,1695 **** --- 1726,1737 ---- break; case T_Attr: _outAttr(str, obj); + break; + case T_Conn: + _outConn(str, obj); + break; + case T_HierClause: + _outHierClause(str, obj); break; default: diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/nodes/readfuncs.c postgresql-7.2.3-evg/src/backend/nodes/readfuncs.c *** postgresql-7.2.3/src/backend/nodes/readfuncs.c Wed Feb 27 03:48:42 2002 --- postgresql-7.2.3-evg/src/backend/nodes/readfuncs.c Fri Nov 22 14:48:23 2002 *************** *** 201,206 **** --- 201,209 ---- token = pg_strtok(&length); /* skip :limitCount */ local_node->limitCount = nodeRead(true); + token = pg_strtok(&length); /* skip :hierClause */ + local_node->hierClause = nodeRead(true); + token = pg_strtok(&length); /* skip :setOperations */ local_node->setOperations = nodeRead(true); *************** *** 211,216 **** --- 214,253 ---- } /* ---------------- + * _readHierClause + * ---------------- + */ + static HierClause * + _readHierClause(void) + { + HierClause *local_node; + char *token; + int length; + + local_node = makeNode(HierClause); + + token = pg_strtok(&length); /* skip :startQual */ + local_node->startQual = (Node *) nodeRead(true); /* get startQual */ + + token = pg_strtok(&length); /* skip :childQual */ + local_node->childQual = (Node *) nodeRead(true); /* get startQual */ + + token = pg_strtok(&length); /* skip :parent */ + token = pg_strtok(&length); /* get parent */ + local_node->parent = atoui(token); + + token = pg_strtok(&length); /* skip :child */ + token = pg_strtok(&length); /* get child */ + local_node->child = atoui(token); + + token = pg_strtok(&length); /* skip :connop */ + token = pg_strtok(&length); /* get connop */ + local_node->connOp = atooid(token); + + return local_node; + } + + /* ---------------- * _readSortClause * ---------------- */ *************** *** 652,657 **** --- 689,733 ---- } /* ---------------- + * _readConn + * + * Conn is a subclass of Plan + * ---------------- + */ + static Conn * + _readConn(void) + { + Conn *local_node; + char *token; + int length; + + local_node = makeNode(Conn); + + _getPlan((Plan *) local_node); + + token = pg_strtok(&length); /* skip :startQual */ + local_node->startQual = (List *) nodeRead(true); /* get startQual */ + + token = pg_strtok(&length); /* skip :childQual */ + local_node->childQual = (List *) nodeRead(true); /* get startQual */ + + token = pg_strtok(&length); /* skip :parent */ + token = pg_strtok(&length); /* get parent */ + local_node->parent = atoui(token); + + token = pg_strtok(&length); /* skip :child */ + token = pg_strtok(&length); /* get child */ + local_node->child = atoui(token); + + token = pg_strtok(&length); /* skip :connop */ + token = pg_strtok(&length); /* get connop */ + local_node->connOp = atoui(token); + + return local_node; + return local_node; + } + + /* ---------------- * _readSort * * Sort is a subclass of Plan *************** *** 954,959 **** --- 1030,1039 ---- token = pg_strtok(&length); /* eat :varoattno */ local_node->varoattno = atoi(token); + token = pg_strtok(&length); /* eat :varfake */ + token = pg_strtok(&length); /* get varfake */ + local_node->varfake = atoi(token); + return local_node; } *************** *** 2022,2027 **** --- 2102,2111 ---- return_value = _readNullTest(); else if (length == 11 && strncmp(token, "BOOLEANTEST", length) == 0) return_value = _readBooleanTest(); + else if (length == 4 && strncmp(token, "CONN", length) == 0) + return_value = _readConn(); + else if (length == 10 && strncmp(token, "HIERCLAUSE", length) == 0) + return_value = _readHierClause(); else elog(ERROR, "badly formatted planstring \"%.10s\"...", token); diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/optimizer/plan/createplan.c postgresql-7.2.3-evg/src/backend/optimizer/plan/createplan.c *** postgresql-7.2.3/src/backend/optimizer/plan/createplan.c Sun Oct 28 10:25:44 2001 --- postgresql-7.2.3-evg/src/backend/optimizer/plan/createplan.c Fri Nov 22 14:48:23 2002 *************** *** 779,784 **** --- 779,785 ---- case T_IndexScan: case T_Material: case T_Sort: + case T_Conn: /* OK, these inner plans support mark/restore */ break; *************** *** 1210,1215 **** --- 1211,1242 ---- node->scanrelid = scanrelid; node->scanstate = (CommonScanState *) NULL; + return node; + } + + Conn * + make_conn(Query *root, + List *tlist, + Plan *lefttree) + { + Conn *node = makeNode(Conn); + Plan *plan = &node->plan; + HierClause *hier = (HierClause *)root->hierClause; + + plan->startup_cost = lefttree->total_cost; /* i will think about it later */ + plan->total_cost = lefttree->total_cost*3; /* just now, triple total cost */ + plan->plan_rows = lefttree->plan_rows; + plan->plan_width = lefttree->plan_width; + plan->state = (EState *) NULL; + plan->targetlist = tlist; + plan->qual = root->havingQual; + plan->lefttree = lefttree; + plan->righttree = NULL; + node->startQual = hier->startQual; + node->childQual = hier->childQual; + node->parent = hier->parent; + node->child = hier->child; + node->connOp = hier->connOp; return node; } diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/optimizer/plan/planner.c postgresql-7.2.3-evg/src/backend/optimizer/plan/planner.c *** postgresql-7.2.3/src/backend/optimizer/plan/planner.c Tue Dec 11 02:54:12 2001 --- postgresql-7.2.3-evg/src/backend/optimizer/plan/planner.c Fri Nov 22 14:48:23 2002 *************** *** 39,45 **** #define EXPRKIND_TARGET 0 #define EXPRKIND_WHERE 1 #define EXPRKIND_HAVING 2 ! static Node *pull_up_subqueries(Query *parse, Node *jtnode); static bool is_simple_subquery(Query *subquery); --- 39,45 ---- #define EXPRKIND_TARGET 0 #define EXPRKIND_WHERE 1 #define EXPRKIND_HAVING 2 ! #define EXPRKIND_STARTWITH 3 static Node *pull_up_subqueries(Query *parse, Node *jtnode); static bool is_simple_subquery(Query *subquery); *************** *** 173,178 **** --- 173,184 ---- parse->havingQual = preprocess_expression(parse, parse->havingQual, EXPRKIND_HAVING); + if(parse->hierClause){ + ((HierClause *)parse->hierClause)->startQual = preprocess_expression(parse, ((HierClause *)parse->hierClause)->startQual, + EXPRKIND_STARTWITH); + ((HierClause *)parse->hierClause)->childQual = preprocess_expression(parse, ((HierClause *)parse->hierClause)->childQual, + EXPRKIND_STARTWITH); + } /* * Check for ungrouped variables passed to subplans in targetlist and * HAVING clause (but not in WHERE or JOIN/ON clauses, since those are *************** *** 202,220 **** * declared as Node *. Also note that contain_agg_clause does not * recurse into sub-selects, which is exactly what we need here. */ ! 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; ! /* * Do the main planning. If we have an inherited target relation, * that needs special processing, else go straight to --- 208,247 ---- * declared as Node *. Also note that contain_agg_clause does not * recurse into sub-selects, which is exactly what we need here. */ ! /* if there's a hierClause, use havingQual to qual post-connected tuples ! * at this point, leave it as it is ! */ ! 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; ! }else{ /* same as above, but another check */ ! newHaving = NIL; ! foreach(lst, (List *) parse->havingQual) ! { ! bool res; ! Node *havingclause = (Node *) lfirst(lst); ! ! res = contain_fake_column(havingclause,FAKE_HIER_LEVEL); ! if (res) ! newHaving = lappend(newHaving, havingclause); ! else ! parse->jointree->quals = (Node *) ! lappend((List *) parse->jointree->quals, havingclause); ! } ! parse->havingQual = (Node *) newHaving; } ! /* * Do the main planning. If we have an inherited target relation, * that needs special processing, else go straight to *************** *** 343,348 **** --- 370,383 ---- ResolveNew(parse->havingQual, 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)->childQual = + ResolveNew(((HierClause *)parse->hierClause)->childQual, + varno, 0, subtlist, CMD_SELECT, 0); + } /* * Pull up any FOR UPDATE markers, too. */ *************** *** 450,461 **** /* * Can't pull up a subquery involving grouping, aggregation, sorting, ! * or limiting. */ if (subquery->hasAggs || subquery->groupClause || subquery->havingQual || subquery->sortClause || subquery->distinctClause || subquery->limitOffset || subquery->limitCount) --- 485,497 ---- /* * Can't pull up a subquery involving grouping, aggregation, sorting, ! * limiting or connecting. */ if (subquery->hasAggs || subquery->groupClause || subquery->havingQual || subquery->sortClause || + subquery->hierClause || subquery->distinctClause || subquery->limitOffset || subquery->limitCount) *************** *** 1170,1175 **** --- 1206,1214 ---- * If aggregate is present, insert the Agg node * * HAVING clause, if any, becomes qual of the Agg node + * + * if there is no hasAggs, then it becomes qual of the + * Conn node */ if (parse->hasAggs) { *************** *** 1178,1188 **** result_plan); /* Note: Agg does not affect any existing sort order of the tuples */ } - else - { - /* If there are no Aggs, we shouldn't have any HAVING qual anymore */ - Assert(parse->havingQual == NULL); - } /* * If we were not able to make the plan come out in the right order, --- 1217,1222 ---- *************** *** 1205,1210 **** --- 1239,1260 ---- } /* + * If there is a CONNECT BY clause + */ + if (parse->hierClause) + { + result_plan = (Plan *) make_connplan(parse, tlist, result_plan); + } + else + { + /* If there are no Aggs and no hierClause , we shouldn't have + * any HAVING qual anymore + */ + if(!parse->hasAggs) Assert(parse->havingQual == NULL); + } + + + /* * Finally, if there is a LIMIT/OFFSET clause, add the LIMIT node. */ if (parse->limitOffset || parse->limitCount) *************** *** 1268,1274 **** * If we're not grouping or aggregating, nothing to do here; * query_planner should receive the unmodified target list. */ ! if (!parse->hasAggs && !parse->groupClause && !parse->havingQual) return tlist; /* --- 1318,1324 ---- * If we're not grouping or aggregating, nothing to do here; * query_planner should receive the unmodified target list. */ ! if (!parse->hasAggs && !parse->groupClause && !(parse->havingQual && !parse->hierClause)) return tlist; /* *************** *** 1428,1433 **** --- 1478,1493 ---- Assert(keyno > 0); return (Plan *) make_sort(parse, sort_tlist, plannode, keyno); + } + /* + * make_connplan + * Add a Conn node to implement an explicit CONNECT BY clause. + */ + Plan * + make_connplan(Query *parse, List *tlist, Plan *plannode) + { + + return (Plan *) make_conn(parse, tlist, plannode); } /* diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/optimizer/plan/setrefs.c postgresql-7.2.3-evg/src/backend/optimizer/plan/setrefs.c *** postgresql-7.2.3/src/backend/optimizer/plan/setrefs.c Mon Nov 5 21:46:26 2001 --- postgresql-7.2.3-evg/src/backend/optimizer/plan/setrefs.c Fri Nov 22 14:48:23 2002 *************** *** 23,28 **** --- 23,30 ---- #include "optimizer/planmain.h" #include "optimizer/tlist.h" + #include "catalog/pg_type.h" + typedef struct { List *outer_tlist; *************** *** 93,98 **** --- 95,107 ---- fix_expr_references(plan, (Node *) plan->targetlist); fix_expr_references(plan, (Node *) plan->qual); 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)->childQual); + break; case T_IndexScan: fix_expr_references(plan, (Node *) plan->targetlist); fix_expr_references(plan, (Node *) plan->qual); *************** *** 336,341 **** --- 345,362 ---- subvarno, subplan_targetlist, tlist_has_non_vars); + if(IsA( plan, Conn)){ + ((Conn *)plan)->startQual = (List *) + replace_vars_with_subplan_refs((Node *) ((Conn *)plan)->startQual, + subvarno, + subplan_targetlist, + tlist_has_non_vars); + ((Conn *)plan)->childQual = (List *) + replace_vars_with_subplan_refs((Node *) ((Conn *)plan)->childQual, + subvarno, + subplan_targetlist, + tlist_has_non_vars); + } } /* *************** *** 469,481 **** Resdom *resdom; Var *newvar; ! resdom = tlist_member((Node *) var, context->subplan_targetlist); ! if (!resdom) ! elog(ERROR, "replace_vars_with_subplan_refs: variable not in subplan target list"); ! newvar = (Var *) copyObject(var); ! newvar->varno = context->subvarno; ! newvar->varattno = resdom->resno; ! return (Node *) newvar; } /* Try matching more complex expressions too, if tlist has any */ if (context->tlist_has_non_vars) --- 490,514 ---- Resdom *resdom; Var *newvar; ! if(var->varfake == FAKE_HIER_LEVEL){ ! /* var here is the '_level_' field which ! * implemented as CONST node on lower layer, so make same and compare, ! * if ok no need in mutating, just pass through*/ ! Const *c = makeConst(INT4OID,sizeof(int4),Int32GetDatum(0),false,true,false,false); ! resdom = tlist_member((Node *) c, context->subplan_targetlist); ! if(!resdom || var->varattno != resdom->resno) ! elog(ERROR, "replace_vars_with_subplan_refs: variable not in subplan target list"); ! /* is there real need to make copy if var not modified? */ ! return node; ! }else{ ! resdom = tlist_member((Node *) var, context->subplan_targetlist); ! if (!resdom) ! elog(ERROR, "replace_vars_with_subplan_refs: variable not in subplan target list"); ! newvar = (Var *) copyObject(var); ! newvar->varno = context->subvarno; ! newvar->varattno = resdom->resno; ! return (Node *) newvar; ! } } /* Try matching more complex expressions too, if tlist has any */ if (context->tlist_has_non_vars) diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/optimizer/plan/subselect.c postgresql-7.2.3-evg/src/backend/optimizer/plan/subselect.c *** postgresql-7.2.3/src/backend/optimizer/plan/subselect.c Fri Nov 30 23:24:15 2001 --- postgresql-7.2.3-evg/src/backend/optimizer/plan/subselect.c Fri Nov 22 14:48:23 2002 *************** *** 353,358 **** --- 353,359 ---- break; case T_Material: case T_Sort: + case T_Conn: /* * Don't add another Material node if there's one *************** *** 682,687 **** --- 683,689 ---- case T_SeqScan: case T_Material: case T_Sort: + case T_Conn: case T_Unique: case T_SetOp: case T_Limit: diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/optimizer/prep/prepkeyset.c postgresql-7.2.3-evg/src/backend/optimizer/prep/prepkeyset.c *** postgresql-7.2.3/src/backend/optimizer/prep/prepkeyset.c Mon Nov 5 21:46:26 2001 --- postgresql-7.2.3-evg/src/backend/optimizer/prep/prepkeyset.c Fri Nov 22 14:48:23 2002 *************** *** 73,78 **** --- 73,79 ---- origNode->unionClause || origNode->unionall || origNode->hasSubLinks || + origNode->hierClause || origNode->commandType != CMD_SELECT) return; diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/optimizer/util/clauses.c postgresql-7.2.3-evg/src/backend/optimizer/util/clauses.c *** postgresql-7.2.3/src/backend/optimizer/util/clauses.c Thu Jan 3 22:01:59 2002 --- postgresql-7.2.3-evg/src/backend/optimizer/util/clauses.c Fri Nov 22 14:48:23 2002 *************** *** 45,51 **** --- 45,57 ---- List *groupClauses; } check_subplans_for_ungrouped_vars_context; + typedef struct + { + FakeType fake; + }contain_fake_column_context; + static bool contain_agg_clause_walker(Node *node, void *context); + static bool contain_fake_column_walker(Node *node, void *context); static bool pull_agg_clause_walker(Node *node, List **listptr); static bool contain_iter_clause_walker(Node *node, void *context); static bool contain_subplans_walker(Node *node, void *context); *************** *** 450,455 **** --- 456,495 ---- (void *) listptr); } + /***************************************************************************** + * Connect by manipulation + *****************************************************************************/ + + /* + * contain_fake_var + * Recursively search for fake var nodes within a clause. + * + * Returns true if any such var found. + */ + bool + contain_fake_column(Node *clause,FakeType ftype) + { + bool result; + contain_fake_column_context *ctx = palloc(sizeof(contain_fake_column_context)); + + ctx->fake = ftype; + result = contain_fake_column_walker(clause, ctx); + pfree(ctx); + return result; + } + + static bool + contain_fake_column_walker(Node *node, void *context) + { + contain_fake_column_context *ctx = (contain_fake_column_context *)context; + + if (node == NULL) + return false; + if (IsA(node, Var) && ((Var *)node)->varfake == ctx->fake) + return true; /* abort the tree traversal and return + * true */ + return expression_tree_walker(node, contain_fake_column_walker, context); + } /***************************************************************************** * Iter clause manipulation *************** *** 1827,1832 **** --- 1867,1881 ---- return true; } break; + case T_HierClause: + { + HierClause *hier = (HierClause *)node; + if (walker(hier->startQual, context)) + return true; + if (walker(hier->childQual, context)) + return true; + } + break; default: elog(ERROR, "expression_tree_walker: Unexpected node type %d", nodeTag(node)); *************** *** 1865,1870 **** --- 1914,1922 ---- return true; if (walker(query->havingQual, context)) return true; + if (walker(query->hierClause, context)) + return true; + if (visitQueryRTEs) { List *rt; diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/parser/analyze.c postgresql-7.2.3-evg/src/backend/parser/analyze.c *** postgresql-7.2.3/src/backend/parser/analyze.c Wed Feb 27 03:48:43 2002 --- postgresql-7.2.3-evg/src/backend/parser/analyze.c Fri Nov 22 14:48:23 2002 *************** *** 1964,1969 **** --- 1964,1971 ---- /* transform WHERE */ qual = transformWhereClause(pstate, stmt->whereClause); + /* 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. *************** *** 1988,1994 **** qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasAggs = pstate->p_hasAggs; ! if (pstate->p_hasAggs || qry->groupClause || qry->havingQual) parseCheckAggregates(pstate, qry, qual); qry->rtable = pstate->p_rtable; --- 1990,1997 ---- qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasAggs = pstate->p_hasAggs; ! ! if (pstate->p_hasAggs || qry->groupClause || (qry->havingQual && !qry->hierClause) ) parseCheckAggregates(pstate, qry, qual); qry->rtable = pstate->p_rtable; diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/parser/gram.y postgresql-7.2.3-evg/src/backend/parser/gram.y *** postgresql-7.2.3/src/backend/parser/gram.y Sat Mar 9 21:41:04 2002 --- postgresql-7.2.3-evg/src/backend/parser/gram.y Fri Nov 22 14:48:23 2002 *************** *** 284,289 **** --- 284,292 ---- %type constraints_set_namelist %type constraints_set_mode + %type startwith_clause opt_connect_qual + %type connectby_clause hier_clause + /* * If you make any token changes, remember to: * - use "yacc -d" and update parse.h *************** *** 363,368 **** --- 366,373 ---- TEMP, TEMPLATE, TOAST, TRUNCATE, TRUSTED, UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION + %token CONNECT + /* The grammar thinks these are keywords, but they are not in the keywords.c * list and so can never be entered directly. The filter in parser.c * creates these tokens when required. *************** *** 3561,3567 **** */ simple_select: SELECT opt_distinct target_list into_clause from_clause where_clause ! group_clause having_clause { SelectStmt *n = makeNode(SelectStmt); n->distinctClause = $2; --- 3566,3572 ---- */ 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; *************** *** 3572,3578 **** n->fromClause = $5; n->whereClause = $6; n->groupClause = $7; ! n->havingClause = $8; $$ = (Node *)n; } | select_clause UNION opt_all select_clause --- 3577,3587 ---- n->fromClause = $5; n->whereClause = $6; n->groupClause = $7; ! n->havingClause = $9; ! n->hierClause = $8; ! if(n->groupClause!=NULL && n->hierClause !=NULL){ ! elog(ERROR,"CONNECT BY and GROUP BY can't be used together"); ! } $$ = (Node *)n; } | select_clause UNION opt_all select_clause *************** *** 3589,3594 **** --- 3598,3625 ---- } ; + hier_clause: connectby_clause startwith_clause + { + $$ = makeList2($1,$2); + } + | startwith_clause connectby_clause + { + $$ = makeList2($2,$1); + } + | /* EMPTY */ { $$ = NULL; } + ; + + connectby_clause: CONNECT BY c_expr PRIOR c_expr opt_connect_qual + { + $$ = makeList3($3,$5,$6); + } + ; + opt_connect_qual: AND a_expr { $$ = $2; } + | /* EMPTY */ { $$ = NULL; } + ; + startwith_clause: START WITH a_expr { $$ = $3; } + ; + /* easy way to return two values. Can someone improve this? bjm */ into_clause: INTO OptTempTableName { $$ = $2; } | /*EMPTY*/ { $$ = makeList1(makeInteger(FALSE)); } *************** *** 5836,5842 **** | PATH_P { $$ = "path"; } | PENDANT { $$ = "pendant"; } | PRECISION { $$ = "precision"; } - | PRIOR { $$ = "prior"; } | PRIVILEGES { $$ = "privileges"; } | PROCEDURAL { $$ = "procedural"; } | PROCEDURE { $$ = "procedure"; } --- 5867,5872 ---- *************** *** 5861,5867 **** | SET { $$ = "set"; } | SHARE { $$ = "share"; } | SHOW { $$ = "show"; } - | START { $$ = "start"; } | STATEMENT { $$ = "statement"; } | STATISTICS { $$ = "statistics"; } | STDIN { $$ = "stdin"; } --- 5891,5896 ---- *************** *** 5981,5986 **** --- 6010,6016 ---- | CHECK { $$ = "check"; } | COLLATE { $$ = "collate"; } | COLUMN { $$ = "column"; } + | CONNECT { $$ = "connect"; } | CONSTRAINT { $$ = "constraint"; } | CURRENT_DATE { $$ = "current_date"; } | CURRENT_TIME { $$ = "current_time"; } *************** *** 6020,6025 **** --- 6050,6056 ---- | SELECT { $$ = "select"; } | SESSION_USER { $$ = "session_user"; } | SOME { $$ = "some"; } + | START { $$ = "start"; } | TABLE { $$ = "table"; } | THEN { $$ = "then"; } | TO { $$ = "to"; } *************** *** 6031,6036 **** --- 6062,6068 ---- | USING { $$ = "using"; } | WHEN { $$ = "when"; } | WHERE { $$ = "where"; } + | PRIOR { $$ = "prior"; } ; diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/parser/keywords.c postgresql-7.2.3-evg/src/backend/parser/keywords.c *** postgresql-7.2.3/src/backend/parser/keywords.c Wed Oct 10 05:02:42 2001 --- postgresql-7.2.3-evg/src/backend/parser/keywords.c Fri Nov 22 14:48:23 2002 *************** *** 71,76 **** --- 71,77 ---- {"comment", COMMENT}, {"commit", COMMIT}, {"committed", COMMITTED}, + {"connect",CONNECT}, {"constraint", CONSTRAINT}, {"constraints", CONSTRAINTS}, {"copy", COPY}, diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/parser/parse_clause.c postgresql-7.2.3-evg/src/backend/parser/parse_clause.c *** postgresql-7.2.3/src/backend/parser/parse_clause.c Thu Oct 25 10:49:37 2001 --- postgresql-7.2.3-evg/src/backend/parser/parse_clause.c Fri Nov 22 14:48:23 2002 *************** *** 36,43 **** #define ORDER_CLAUSE 0 #define GROUP_CLAUSE 1 #define DISTINCT_ON_CLAUSE 2 ! static char *clauseText[] = {"ORDER BY", "GROUP BY", "DISTINCT ON"}; static void extractUniqueColumns(List *common_colnames, List *src_colnames, List *src_colvars, --- 36,44 ---- #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 extractUniqueColumns(List *common_colnames, List *src_colnames, List *src_colvars, *************** *** 968,973 **** --- 969,1036 ---- } return glist; + } + + /* + * transformConnectClause - + * transform a Connect By clause + * + */ + Node * + transformHierClause(ParseState *pstate, Query *qry, List *hier) + { + List *clist = NIL; + TargetEntry *tle,*tle1; + HierClause *node; + List *tlist=qry->targetList; + Node *n; + Const *cst; + + if(!hier) return NULL; + + node=makeNode(HierClause); + + clist=lfirst(hier);//get conn_list + tle = findTargetlistEntry(pstate, lfirst(clist), + tlist, CONNECT_CLAUSE); + tle1 = findTargetlistEntry(pstate, lsecond(clist), + tlist, CONNECT_CLAUSE); + + node->parent = tle->resdom->resno; + node->child = tle1->resdom->resno; + node->connOp = compatible_oper_opid("=",tle->resdom->restype, + tle1->resdom->restype,true); + if(node->connOp == InvalidOid){ + elog(ERROR,"CONNECT BY: can't connect by these fields"); + } + + n=lfirst(lnext(lnext(clist))); + if(n){ + node->childQual=transformWhereClause(pstate,n);//lthird + }else{ + node->childQual=NULL; + } + node->startQual=transformWhereClause(pstate,lsecond(hier)); + + /* add a field '_level_', in result there will be deepness of tuple + * in result tree + */ + /* add a const node to modify it later, in utils/tupleconn.c */ + cst=makeNode(Const); + cst->consttype = INT4OID; + cst->constlen = sizeof(int4); + cst->constvalue = Int32GetDatum(0); + cst->constisnull = FALSE; + cst->constbyval = TRUE; + cst->constisset = FALSE; + cst->constiscast = FALSE; + + tle = transformTargetEntry(pstate, (Node *)cst, NULL, "_level_", false); + lappend(tlist, tle); + + pstate->p_hierLevelColIdx = tle->resdom->resno; + + return (Node *)node; } /* diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/parser/parse_expr.c postgresql-7.2.3-evg/src/backend/parser/parse_expr.c *** postgresql-7.2.3/src/backend/parser/parse_expr.c Tue Nov 13 00:05:24 2001 --- postgresql-7.2.3-evg/src/backend/parser/parse_expr.c Fri Nov 22 14:48:23 2002 *************** *** 664,669 **** --- 664,681 ---- } } + if (result == NULL && pstate->p_hierLevelColIdx!=0 && !strcmp(ident->name,"_level_")){ + Var *var = makeNode(Var); + + var->varno = 0; + var->varattno = pstate->p_hierLevelColIdx; + var->vartype = INT4OID; + var->varlevelsup = 0; + var->vartypmod = -1; + var->varfake = FAKE_HIER_LEVEL; + result = (Node *)var; + } + if (result == NULL) elog(ERROR, "Attribute '%s' not found", ident->name); diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/port/Makefile postgresql-7.2.3-evg/src/backend/port/Makefile *** postgresql-7.2.3/src/backend/port/Makefile Thu Jan 1 04:00:00 1970 --- postgresql-7.2.3-evg/src/backend/port/Makefile Fri Nov 22 14:48:23 2002 *************** *** 0 **** --- 1,79 ---- + # Generated automatically from Makefile.in by configure. + #------------------------------------------------------------------------- + # + # Makefile-- + # Makefile for the port-specific subsystem of the backend + # + # We have two different modes of operation: 1) put stuff specific to Port X + # in subdirectory X and have that subdirectory's make file make it all, and + # 2) use conditional statements in the present make file to include what's + # necessary for a specific port in our own output. (1) came first, but (2) + # is superior for many things, like when the same thing needs to be done for + # multiple ports and you don't want to duplicate files in multiple + # subdirectories. Much of the stuff done via Method 1 today should probably + # be converted to Method 2. + # + # IDENTIFICATION + # $Header: /cvsroot/pgsql-server/src/backend/port/Attic/Makefile.in,v 1.30 2001/12/20 21:23:05 momjian Exp $ + # + #------------------------------------------------------------------------- + + subdir = src/backend/port + top_builddir = ../../.. + include $(top_builddir)/src/Makefile.global + + OBJS = dynloader.o + OBJS+= + OBJS+= + ifdef STRDUP + OBJS += $(top_builddir)/src/utils/strdup.o + endif + ifeq ($(PORTNAME), qnx4) + OBJS += getrusage.o qnx4/SUBSYS.o + endif + ifeq ($(PORTNAME), beos) + OBJS += beos/SUBSYS.o + endif + ifeq ($(PORTNAME), darwin) + OBJS += darwin/SUBSYS.o + endif + + all: SUBSYS.o + + SUBSYS.o: $(OBJS) + $(LD) $(LDREL) $(LDOUT) SUBSYS.o $(OBJS) + + qnx4/SUBSYS.o: qnx4.dir + + qnx4.dir: + $(MAKE) -C qnx4 all + + beos/SUBSYS.o: beos.dir + + beos.dir: + $(MAKE) -C beos all + + darwin/SUBSYS.o: darwin.dir + + darwin.dir: + $(MAKE) -C darwin all + + tas.o: tas.s + $(CC) $(CFLAGS) -c $< + + $(top_builddir)/src/utils/strdup.o: + $(MAKE) -C $(top_builddir)/src/utils strdup.o + + + distclean clean: + rm -f SUBSYS.o $(OBJS) + $(MAKE) -C beos clean + $(MAKE) -C darwin clean + $(MAKE) -C qnx4 clean + + depend dep: + $(CC) -MM $(CFLAGS) *.c >depend + + ifeq (depend,$(wildcard depend)) + include depend + endif diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/utils/sort/Makefile postgresql-7.2.3-evg/src/backend/utils/sort/Makefile *** postgresql-7.2.3/src/backend/utils/sort/Makefile Thu Aug 31 21:10:59 2000 --- postgresql-7.2.3-evg/src/backend/utils/sort/Makefile Fri Nov 22 14:48:23 2002 *************** *** 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 -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/backend/utils/sort/tupleconn.c postgresql-7.2.3-evg/src/backend/utils/sort/tupleconn.c *** postgresql-7.2.3/src/backend/utils/sort/tupleconn.c Thu Jan 1 04:00:00 1970 --- postgresql-7.2.3-evg/src/backend/utils/sort/tupleconn.c Fri Nov 22 14:48:23 2002 *************** *** 0 **** --- 1,899 ---- + /*------------------------------------------------------------------------- + * + * tupleconn.c + * Routines for tuple connecting. + * + * Based on tuplestore.c, tuplesort.c. (c) by Evgen Potemkin evgent@terminal.ru, 11.2002 + * + *------------------------------------------------------------------------- + */ + + #include "postgres.h" + + #include "access/heapam.h" + #include "storage/buffile.h" + #include "utils/tupleconn.h" + #include "utils/tuplesort.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/fmgroids.h" + #include "utils/lsyscache.h" + #include "utils/syscache.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; + /* + * 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 + */ + 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 parent,child,level; /* tuple's 'parent','child' and '_level_' attribute numbers */ + FmgrInfo connFn; /* comparation function */ + + OutStore *out_store; /* array of info about tuples in buffile for faster random access */ + + 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 */ + }; + + + #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)) + + + + 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); + + ConnectTree *add_next(ConnectTree *tr,int tup,int level); + ConnectTree *add_sub(ConnectTree *tr,int tup,int level); + + + /* + * 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->tupcount = 0; + if (maxKBytes > 0) + state->memtupsize = 1024; /* initial guess */ + else + state->memtupsize = 1; /* won't really need any space */ + state->memtuples = (void **) palloc(state->memtupsize * sizeof(void *)); + state->used = (char *) palloc(state->memtupsize * sizeof(char)); + + state->pntlast=0; + state->chldlast=0; + state->pntsize=1024; /* initial guess */ + state->chldsize=1024; /* initial guess */ + state->pnt=(ConnectTree **)palloc(state->pntsize * sizeof(ConnectTree *)); + state->chld=(ConnectTree **)palloc(state->pntsize * sizeof(ConnectTree *)); + return state; + } + + Tupleconnstate * + tupleconn_begin_heap(int maxKBytes, TupleDesc tupDesc, + Oid connOp, AttrNumber parent,AttrNumber child) + { + RegProcedure connFunc; + SortFunctionKind connFnKind; + + Tupleconnstate *state = tupleconn_begin_common(maxKBytes); + + state->copytup = copytup_heap; + state->writetup = writetup_heap; + state->readtup = readtup_heap; + + state->tupDesc=tupDesc; + state->parent=parent; + state->child=child; + + SelectConnFunction(connOp, &connFunc, + &connFnKind); + if(connFnKind != SORTFUNC_CMP){ + elog(ERROR,"tupleconn: Can't find suitable function for comparation"); + } + fmgr_info(connFunc,&state->connFn); + return state; + } + + /* + * tupleconn_end + * + * Release resources and clean up. + */ + void + tupleconn_end(Tupleconnstate *state) + { + int i; + ConnectTree *old; + + /* common frees */ + pfree(state->pnt); + pfree(state->chld); + pfree(state->used); + + /* free tree, if any */ + state->last = state->head; + if(state->last){ + state->skip_node = false; + while(1) { + if(!state->skip_node && state->last->sub) state->last=state->last->sub; + else if(state->last->next){ + old = state->last; + state->last=state->last->next; + state->skip_node=false; + pfree(old); + } else if(state->last->sup){ + old = state->last; + state->last=state->last->sup; + state->skip_node=true; + pfree(old); + } else { + break; + } + } + pfree(state->last); + state->head = state->last = NULL; + } + + /* 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 + state->last = add_next(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) + { + /* 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)); + } + state->memtuples[state->tupcount++] = tuple; + + /* + * Done if we still fit in available memory. + */ + if (!LACKMEM(state)) + return; + + /* + * Nope; time to switch to tape-based operation. + */ + state->myfile = BufFileCreateTemp(); + state->status = TCS_WRITEFILE; + state->out_store = palloc(state->memtupsize * sizeof(OutStore)); + dumptuples(state); + break; + case TCS_WRITEFILE: + if (state->tupcount >= state->memtupsize) + { + /* Grow the arrays as needed. */ + state->memtupsize *= 2; + state->out_store = (OutStore *) + repalloc(state->out_store, + state->memtupsize * sizeof(void *)); + state->used = (char *) + repalloc(state->used, + state->memtupsize * sizeof(char)); + } + 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. + * + */ + void + tupleconn_performconn(Tupleconnstate *state){ + int ok=0,i,j,ti,level=1; + ConnectTree **t; + int32 is_parent; + Datum dt1,dt2; + bool p_isnull,isnull,conn; + AttrNumber attno1,attno2; + HeapTuple pnt,chld; + TupleDesc tupDesc; + + 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)"); + } + + tupDesc=state->tupDesc; + + attno1=state->child; + attno2=state->parent; + + while(!ok){ + ok=1; + for(i=0;itupcount;i++){//scan through array of unconnected tuples + /* skip already connected and null tuples */ + if(!state->used[i]){ + /* get tuple for connecting */ + if(state->status == TCS_READMEM) + chld=(HeapTuple)state->memtuples[i]; + else chld=READTUP(state,i); + + dt1=heap_getattr(chld, attno1, tupDesc, &isnull); + conn=false; + if(!isnull){ + for(j=0;jpntlast && !conn;j++){//scan through nodes array of previous level + /* get parent tuple */ + if(state->status == TCS_READMEM) + pnt=(HeapTuple)state->memtuples[state->pnt[j]->tuple]; + else pnt=state->out_store[state->pnt[j]->tuple].tup; + + Assert(pnt!=NULL); /*elog(ERROR,"tupleconn: parent tuple is null (internal error)");*/ + dt2=heap_getattr(pnt, attno2, tupDesc, &p_isnull); + if(!p_isnull){ + 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 tree)*/ + state->chld[state->chldlast++]=add_sub(state->pnt[j],i,level+1); + state->used[i]=1; + if(state->chldlast==state->chldsize){ + /* grow array of connected tuples as necessary */ + state->chldsize *= 2; + state->chld = (ConnectTree **) + repalloc(state->chld, + state->chldsize * sizeof(ConnectTree **)); + } + } + } + } + }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; + } + + /* this tuple will not become parent for next level, + * free it for saving memory + */ + if(state->status == TCS_READFILE && + (state->used[i]!=1 || (state->used[i]==1 && isnull))){ + pfree(chld); + state->out_store[i].tup=NULL; + FREEMEM(state,state->out_store[i].len); + } + } + } + /* swap pnt & chld arrays */ + t=state->pnt; + ti=state->pntsize; + + if(state->status == TCS_READFILE){ + /* remove unneeded parent nodes */ + for(i=0;ipntlast;i++){ + pfree(state->out_store[state->pnt[i]->tuple].tup); + state->out_store[state->pnt[i]->tuple].tup=NULL; + FREEMEM(state,state->out_store[state->pnt[i]->tuple].len); + } + } + state->pnt=state->chld; + state->pntsize=state->chldsize; + state->pntlast=state->chldlast; + + state->chld=t; + state->chldsize=ti; + state->chldlast=0; + + level++; + } + + /* find _level_ attribute */ + for(i=0;itupDesc->natts;i++){ + if(!strcmp(state->tupDesc->attrs[i]->attname.data,"_level_")) state->level=i; + } + } + + /* set _level_ column to proper value */ + void * + setlevel(Tupleconnstate *state,int level, void *tuple,bool free_it){ + #define REALLOCATTRS 64 + Datum valuesArray[REALLOCATTRS]; + char nullsArray[REALLOCATTRS]; + Datum *values; + char *nulls; + HeapTuple newtup,tup = (HeapTuple)tuple; + TupleDesc tdesc = state->tupDesc; + int natts,i; + + + if(!tuple) return NULL; + natts = tdesc->natts; + + if(natts>REALLOCATTRS){ + values = palloc(natts * sizeof(Datum)); + nulls = palloc(natts * sizeof(char)); + }else{ + values = valuesArray; + nulls = nullsArray; + } + /* substitute attr _level_ with real value */ + for (i = 0; i < natts; i++){ + bool isnull; + 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] = ' '; + } + } + tup = heap_formtuple(state->tupDesc,values,nulls); + + if(natts>REALLOCATTRS){ + pfree(values); + pfree(nulls); + } + if(free_it){ + newtup = heap_copytuple(tup); + tup = newtup; + FREEMEM(state,((HeapTuple)tuple)->t_len+HEAPTUPLESIZE); + pfree(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 + */ + void * + tupleconn_gettuple(Tupleconnstate *state, + bool *should_free) + { + void *tup=NULL; + int level=0; + + 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]; + }else{ + tup=READTUP(state,state->last->tuple); + state->out_store[state->last->tuple].tup=NULL;/* will be freed on upper level,not in _end */ + } + 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; + } + } + return setlevel(state,level,tup,*should_free); + } + + /* + * 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 _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) + { + Assert(state->randomAccess); + + /* 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) + { + Assert(state->randomAccess); + + /* 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 + */ + + ConnectTree * + add_next(ConnectTree *tr,int tup,int level){ + ConnectTree *t; + + t=palloc(sizeof(ConnectTree)); + memset(t,0,sizeof(ConnectTree)); + t->tuple=tup; + t->level=level; + if(tr){ + tr->next=t; + t->prev=tr; + } + return t; + } + + ConnectTree * + add_sub(ConnectTree *tr,int tup,int level){ + ConnectTree *t,*t1; + + t=palloc(sizeof(ConnectTree)); + 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 + */ + + /* + * 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 + */ + + void + SelectConnFunction(Oid sortOperator, + RegProcedure *sortFunction, + SortFunctionKind *kind) + { + Relation relation; + HeapScanDesc scan; + ScanKeyData skey[1]; + HeapTuple tuple; + Form_pg_operator optup; + Oid opclass = InvalidOid; + + /* + * Scan pg_amop to see if the target operator is registered as the "<" + * or ">" operator of any btree opclass. It's possible that it might + * be registered both ways (eg, if someone were to build a "reverse + * sort" opclass for some reason); prefer the "<" case if so. If the + * operator is registered the same way in multiple opclasses, assume + * we can use the associated comparator function from any one. + */ + ScanKeyEntryInitialize(&skey[0], 0x0, + Anum_pg_amop_amopopr, + F_OIDEQ, + ObjectIdGetDatum(sortOperator)); + + relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock); + scan = heap_beginscan(relation, false, SnapshotNow, 1, skey); + + while (HeapTupleIsValid(tuple = heap_getnext(scan, 0))) + { + 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); + + *sortFunction = aform->amproc; + ReleaseSysCache(tuple); + Assert(RegProcedureIsValid(*sortFunction)); + return; + } + } + + /* + * Can't find a comparator, so use the operator as-is. Decide whether + * it is forward or reverse sort by looking at its name (grotty, but + * this only matters for deciding which end NULLs should get sorted + * to). + */ + tuple = SearchSysCache(OPEROID, + ObjectIdGetDatum(sortOperator), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "SelectConnFunction: cache lookup failed for operator %u", + sortOperator); + optup = (Form_pg_operator) GETSTRUCT(tuple); + if (strcmp(NameStr(optup->oprname), "=") == 0) + *kind = SORTFUNC_CMP; + else{ + elog(NOTICE, "SelectConnFunction: using operator '%s' for connecting",NameStr(optup->oprname)); + *kind = SORTFUNC_CMP; + } + *sortFunction = optup->oprcode; + ReleaseSysCache(tuple); + + Assert(RegProcedureIsValid(*sortFunction)); + } diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/include/executor/nodeConn.h postgresql-7.2.3-evg/src/include/executor/nodeConn.h *** postgresql-7.2.3/src/include/executor/nodeConn.h Thu Jan 1 04:00:00 1970 --- postgresql-7.2.3-evg/src/include/executor/nodeConn.h Fri Nov 22 14:48:23 2002 *************** *** 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(Conn *node); + extern bool ExecInitConn(Conn *node, EState *estate, Plan *parent); + extern int ExecCountSlotsConn(Conn *node); + extern void ExecEndConn(Conn *node); + extern void ExecConnMarkPos(Conn *node); + extern void ExecConnRestrPos(Conn *node); + extern void ExecReScanConn(Conn *node, ExprContext *exprCtxt, Plan *parent); + + #endif /* NODECONN_H */ diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/include/nodes/execnodes.h postgresql-7.2.3-evg/src/include/nodes/execnodes.h *** postgresql-7.2.3/src/include/nodes/execnodes.h Thu Nov 22 02:57:01 2001 --- postgresql-7.2.3-evg/src/include/nodes/execnodes.h Fri Nov 22 14:48:23 2002 *************** *** 650,655 **** --- 650,669 ---- } SortState; /* ---------------- + * ConnectState information + * + * conn_Done indicates whether connection has been performed yet + * tupleconnstate private state of tuplesort.c + * ---------------- + */ + typedef struct ConnectState + { + CommonScanState csstate; /* its first field is NodeTag */ + bool conn_Done; + void *tupleconnstate; + } ConnectState; + + /* ---------------- * UniqueState information * * Unique nodes are used "on top of" sort nodes to discard diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/include/nodes/nodes.h postgresql-7.2.3-evg/src/include/nodes/nodes.h *** postgresql-7.2.3/src/include/nodes/nodes.h Mon Nov 5 21:46:34 2001 --- postgresql-7.2.3-evg/src/include/nodes/nodes.h Fri Nov 22 14:48:23 2002 *************** *** 41,46 **** --- 41,47 ---- T_Limit, T_Material, T_Sort, + T_Conn, T_Agg, T_Unique, T_Hash, *************** *** 114,119 **** --- 115,121 ---- T_AggState, T_GroupState, T_SortState, + T_ConnectState, T_UniqueState, T_HashState, T_TidScanState, *************** *** 222,228 **** T_CaseWhen, T_FkConstraint, T_PrivGrantee, ! /* * TAGS FOR FUNCTION-CALL CONTEXT AND RESULTINFO NODES (see fmgr.h) */ --- 224,230 ---- T_CaseWhen, T_FkConstraint, T_PrivGrantee, ! T_HierClause, /* * TAGS FOR FUNCTION-CALL CONTEXT AND RESULTINFO NODES (see fmgr.h) */ diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/include/nodes/parsenodes.h postgresql-7.2.3-evg/src/include/nodes/parsenodes.h *** postgresql-7.2.3/src/include/nodes/parsenodes.h Wed Feb 27 03:48:46 2002 --- postgresql-7.2.3-evg/src/include/nodes/parsenodes.h Fri Nov 22 14:48:23 2002 *************** *** 72,78 **** Node *setOperations; /* set-operation tree if this is top level * of a UNION/INTERSECT/EXCEPT query */ ! /* * 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 --- 72,78 ---- 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 *************** *** 889,895 **** Node *whereClause; /* WHERE qualification */ List *groupClause; /* GROUP BY clauses */ Node *havingClause; /* HAVING conditional-expression */ ! /* * These fields are used in both "leaf" SelectStmts and upper-level * SelectStmts. portalname/binary may only be set at the top level. --- 889,895 ---- 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 * SelectStmts. portalname/binary may only be set at the top level. *************** *** 1356,1360 **** --- 1356,1372 ---- * nodetags...). We have routines that operate interchangeably on both. */ typedef SortClause GroupClause; + + /* + * HierClause - + * representation of CONNECT BY/START WITH clauses + */ + typedef struct HierClause{ + NodeTag type; + AttrNumber parent,child; /* 'parent' and 'child' attribute numbers */ + Node *startQual; /* Quals for heads */ + Node *childQual; /* quals for childs, may be NULL */ + Oid connOp; /* operator for comparing parent & child attrs*/ + } HierClause; #endif /* PARSENODES_H */ diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/include/nodes/plannodes.h postgresql-7.2.3-evg/src/include/nodes/plannodes.h *** postgresql-7.2.3/src/include/nodes/plannodes.h Mon Nov 5 21:46:34 2001 --- postgresql-7.2.3-evg/src/include/nodes/plannodes.h Fri Nov 22 14:48:23 2002 *************** *** 45,50 **** --- 45,51 ---- * * Material MaterialState matstate; * Sort SortState sortstate; + * Conn ConnectState connstate; * Unique UniqueState uniquestate; * SetOp SetOpState setopstate; * Limit LimitState limitstate; *************** *** 353,358 **** --- 354,373 ---- int keycount; SortState *sortstate; } Sort; + + /* ---------------- + * conn node + * ---------------- + */ + typedef struct Conn + { + Plan plan; + ConnectState *connstate; + List *startQual; /* qual conditions for heads */ + List *childQual; /* qual conditions for childs */ + AttrNumber parent,child; /* indexes into the target list */ + Oid connOp; + }Conn; /* ---------------- * unique node diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/include/nodes/primnodes.h postgresql-7.2.3-evg/src/include/nodes/primnodes.h *** postgresql-7.2.3/src/include/nodes/primnodes.h Mon Nov 5 21:46:34 2001 --- postgresql-7.2.3-evg/src/include/nodes/primnodes.h Fri Nov 22 14:48:23 2002 *************** *** 134,139 **** --- 134,144 ---- #define PRS2_OLD_VARNO 1 #define PRS2_NEW_VARNO 2 + typedef enum FakeType + { + FAKE_REAL, FAKE_HIER_LEVEL + } FakeType; + typedef struct Var { NodeTag type; *************** *** 153,158 **** --- 158,164 ---- */ Index varnoold; /* original value of varno, for debugging */ AttrNumber varoattno; /* original value of varattno */ + FakeType varfake; /* either var is fake or real */ } Var; /*-------------------- diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/include/optimizer/clauses.h postgresql-7.2.3-evg/src/include/optimizer/clauses.h *** postgresql-7.2.3/src/include/optimizer/clauses.h Tue Dec 11 02:54:12 2001 --- postgresql-7.2.3-evg/src/include/optimizer/clauses.h Fri Nov 22 14:48:23 2002 *************** *** 42,47 **** --- 42,49 ---- extern bool contain_agg_clause(Node *clause); extern List *pull_agg_clause(Node *clause); + extern bool contain_fake_column(Node *clause,FakeType ftype); + extern bool contain_iter_clause(Node *clause); extern bool contain_subplans(Node *clause); diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/include/optimizer/planmain.h postgresql-7.2.3-evg/src/include/optimizer/planmain.h *** postgresql-7.2.3/src/include/optimizer/planmain.h Mon Nov 5 21:46:34 2001 --- postgresql-7.2.3-evg/src/include/optimizer/planmain.h Fri Nov 22 14:48:23 2002 *************** *** 31,36 **** --- 31,37 ---- extern Append *make_append(List *appendplans, bool isTarget, List *tlist); extern Sort *make_sort(Query *root, List *tlist, Plan *lefttree, int keycount); + extern Conn *make_conn(Query *root, List *tlist, Plan *lefttree); extern Sort *make_sort_from_pathkeys(Query *root, List *tlist, Plan *lefttree, List *pathkeys); extern Agg *make_agg(List *tlist, List *qual, Plan *lefttree); diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/include/optimizer/planner.h postgresql-7.2.3-evg/src/include/optimizer/planner.h *** postgresql-7.2.3/src/include/optimizer/planner.h Mon Nov 5 21:46:34 2001 --- postgresql-7.2.3-evg/src/include/optimizer/planner.h Fri Nov 22 14:48:23 2002 *************** *** 23,27 **** --- 23,29 ---- extern Plan *make_sortplan(Query *parse, List *tlist, Plan *plannode, List *sortcls); + extern Plan *make_connplan(Query *parse, List *tlist, + Plan *plannode); #endif /* PLANNER_H */ diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/include/parser/parse_clause.h postgresql-7.2.3-evg/src/include/parser/parse_clause.h *** postgresql-7.2.3/src/include/parser/parse_clause.h Mon Nov 5 21:46:34 2001 --- postgresql-7.2.3-evg/src/include/parser/parse_clause.h Fri Nov 22 14:48:23 2002 *************** *** 23,28 **** --- 23,29 ---- extern Node *transformWhereClause(ParseState *pstate, Node *where); extern List *transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist); + extern Node *transformHierClause(ParseState *pstate, Query *qry, List *hier); extern List *transformSortClause(ParseState *pstate, List *orderlist, List *targetlist); extern List *transformDistinctClause(ParseState *pstate, List *distinctlist, diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/include/parser/parse_node.h postgresql-7.2.3-evg/src/include/parser/parse_node.h *** postgresql-7.2.3/src/include/parser/parse_node.h Mon Nov 5 21:46:35 2001 --- postgresql-7.2.3-evg/src/include/parser/parse_node.h Fri Nov 22 14:48:23 2002 *************** *** 46,51 **** --- 46,54 ---- bool p_hasSubLinks; bool p_is_insert; bool p_is_update; + + AttrNumber p_hierLevelColIdx; + Relation p_target_relation; RangeTblEntry *p_target_rangetblentry; } ParseState; diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/include/stamp-h postgresql-7.2.3-evg/src/include/stamp-h *** postgresql-7.2.3/src/include/stamp-h Thu Jan 1 04:00:00 1970 --- postgresql-7.2.3-evg/src/include/stamp-h Fri Nov 22 14:48:23 2002 *************** *** 0 **** --- 1 ---- + diff -P -r -d -c --exclude-from=exclude postgresql-7.2.3/src/include/utils/tupleconn.h postgresql-7.2.3-evg/src/include/utils/tupleconn.h *** postgresql-7.2.3/src/include/utils/tupleconn.h Thu Jan 1 04:00:00 1970 --- postgresql-7.2.3-evg/src/include/utils/tupleconn.h Fri Nov 22 14:48:23 2002 *************** *** 0 **** --- 1,62 ---- + /*------------------------------------------------------------------------- + * + * 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" + + /* 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, AttrNumber parent,AttrNumber child); + + 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); + + /* + * 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 */