diff options
Diffstat (limited to 'src/backend/executor')
25 files changed, 13807 insertions, 13117 deletions
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c index 84b33d4f1e1..401924485e0 100644 --- a/src/backend/executor/execAmi.c +++ b/src/backend/executor/execAmi.c @@ -1,32 +1,32 @@ /*------------------------------------------------------------------------- * * execAmi.c-- - * miscellanious executor access method routines + * miscellanious executor access method routines * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execAmi.c,v 1.5 1997/08/19 21:30:51 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execAmi.c,v 1.6 1997/09/07 04:41:09 momjian Exp $ * *------------------------------------------------------------------------- */ /* - * INTERFACE ROUTINES + * INTERFACE ROUTINES * - * ExecOpenScanR \ / amopen - * ExecBeginScan \ / ambeginscan - * ExecCloseR \ / amclose - * ExecInsert \ executor interface / aminsert - * ExecReScanNode / to access methods \ amrescan - * ExecReScanR / \ amrescan - * ExecMarkPos / \ ammarkpos - * ExecRestrPos / \ amrestpos + * ExecOpenScanR \ / amopen + * ExecBeginScan \ / ambeginscan + * ExecCloseR \ / amclose + * ExecInsert \ executor interface / aminsert + * ExecReScanNode / to access methods \ amrescan + * ExecReScanR / \ amrescan + * ExecMarkPos / \ ammarkpos + * ExecRestrPos / \ amrestpos * - * ExecCreatR function to create temporary relations + * ExecCreatR function to create temporary relations * */ -#include <stdio.h> /* for sprintf() */ +#include <stdio.h> /* for sprintf() */ #include "postgres.h" @@ -43,409 +43,430 @@ #include "access/heapam.h" #include "catalog/heap.h" -static Pointer ExecBeginScan(Relation relation, int nkeys, ScanKey skeys, - bool isindex, ScanDirection dir, TimeQual time_range); +static Pointer +ExecBeginScan(Relation relation, int nkeys, ScanKey skeys, + bool isindex, ScanDirection dir, TimeQual time_range); static Relation ExecOpenR(Oid relationOid, bool isindex); /* ---------------------------------------------------------------- - * ExecOpenScanR + * ExecOpenScanR * * old comments: - * Parameters: - * relation -- relation to be opened and scanned. - * nkeys -- number of keys - * skeys -- keys to restrict scanning - * isindex -- if this is true, the relation is the relid of - * an index relation, else it is an index into the - * range table. - * Returns the relation as(relDesc scanDesc) - * If this structure is changed, need to modify the access macros - * defined in execInt.h. + * Parameters: + * relation -- relation to be opened and scanned. + * nkeys -- number of keys + * skeys -- keys to restrict scanning + * isindex -- if this is true, the relation is the relid of + * an index relation, else it is an index into the + * range table. + * Returns the relation as(relDesc scanDesc) + * If this structure is changed, need to modify the access macros + * defined in execInt.h. * ---------------------------------------------------------------- */ void ExecOpenScanR(Oid relOid, - int nkeys, - ScanKey skeys, - bool isindex, - ScanDirection dir, - TimeQual timeRange, - Relation *returnRelation, /* return */ - Pointer *returnScanDesc) /* return */ + int nkeys, + ScanKey skeys, + bool isindex, + ScanDirection dir, + TimeQual timeRange, + Relation * returnRelation, /* return */ + Pointer * returnScanDesc) /* return */ { - Relation relation; - Pointer scanDesc; - - /* ---------------- - * note: scanDesc returned by ExecBeginScan can be either - * a HeapScanDesc or an IndexScanDesc so for now we - * make it a Pointer. There should be a better scan - * abstraction someday -cim 9/9/89 - * ---------------- - */ - relation = ExecOpenR(relOid, isindex); - scanDesc = ExecBeginScan(relation, - nkeys, - skeys, - isindex, - dir, - timeRange); - - if (returnRelation != NULL) - *returnRelation = relation; - if (scanDesc != NULL) - *returnScanDesc = scanDesc; + Relation relation; + Pointer scanDesc; + + /* ---------------- + * note: scanDesc returned by ExecBeginScan can be either + * a HeapScanDesc or an IndexScanDesc so for now we + * make it a Pointer. There should be a better scan + * abstraction someday -cim 9/9/89 + * ---------------- + */ + relation = ExecOpenR(relOid, isindex); + scanDesc = ExecBeginScan(relation, + nkeys, + skeys, + isindex, + dir, + timeRange); + + if (returnRelation != NULL) + *returnRelation = relation; + if (scanDesc != NULL) + *returnScanDesc = scanDesc; } - + /* ---------------------------------------------------------------- - * ExecOpenR + * ExecOpenR * - * returns a relation descriptor given an object id. + * returns a relation descriptor given an object id. * ---------------------------------------------------------------- */ -static Relation +static Relation ExecOpenR(Oid relationOid, bool isindex) { - Relation relation; - relation = (Relation) NULL; - - /* ---------------- - * open the relation with the correct call depending - * on whether this is a heap relation or an index relation. - * ---------------- - */ - if (isindex) { - relation = index_open(relationOid); - } else - relation = heap_open(relationOid); - - if (relation == NULL) - elog(DEBUG, "ExecOpenR: relation == NULL, heap_open failed."); - - return relation; + Relation relation; + + relation = (Relation) NULL; + + /* ---------------- + * open the relation with the correct call depending + * on whether this is a heap relation or an index relation. + * ---------------- + */ + if (isindex) + { + relation = index_open(relationOid); + } + else + relation = heap_open(relationOid); + + if (relation == NULL) + elog(DEBUG, "ExecOpenR: relation == NULL, heap_open failed."); + + return relation; } - + /* ---------------------------------------------------------------- - * ExecBeginScan + * ExecBeginScan * - * beginscans a relation in current direction. + * beginscans a relation in current direction. * - * XXX fix parameters to AMbeginscan (and btbeginscan) - * currently we need to pass a flag stating whether - * or not the scan should begin at an endpoint of - * the relation.. Right now we always pass false - * -cim 9/14/89 + * XXX fix parameters to AMbeginscan (and btbeginscan) + * currently we need to pass a flag stating whether + * or not the scan should begin at an endpoint of + * the relation.. Right now we always pass false + * -cim 9/14/89 * ---------------------------------------------------------------- */ -static Pointer +static Pointer ExecBeginScan(Relation relation, - int nkeys, - ScanKey skeys, - bool isindex, - ScanDirection dir, - TimeQual time_range) + int nkeys, + ScanKey skeys, + bool isindex, + ScanDirection dir, + TimeQual time_range) { - Pointer scanDesc; - - scanDesc = NULL; - - /* ---------------- - * open the appropriate type of scan. - * - * Note: ambeginscan()'s second arg is a boolean indicating - * that the scan should be done in reverse.. That is, - * if you pass it true, then the scan is backward. - * ---------------- - */ - if (isindex) { - scanDesc = (Pointer) index_beginscan(relation, - false, /* see above comment */ - nkeys, - skeys); - } else { - scanDesc = (Pointer) heap_beginscan(relation, - ScanDirectionIsBackward(dir), - time_range, - nkeys, - skeys); - } - - if (scanDesc == NULL) - elog(DEBUG, "ExecBeginScan: scanDesc = NULL, heap_beginscan failed."); - - - return scanDesc; + Pointer scanDesc; + + scanDesc = NULL; + + /* ---------------- + * open the appropriate type of scan. + * + * Note: ambeginscan()'s second arg is a boolean indicating + * that the scan should be done in reverse.. That is, + * if you pass it true, then the scan is backward. + * ---------------- + */ + if (isindex) + { + scanDesc = (Pointer) index_beginscan(relation, + false, /* see above comment */ + nkeys, + skeys); + } + else + { + scanDesc = (Pointer) heap_beginscan(relation, + ScanDirectionIsBackward(dir), + time_range, + nkeys, + skeys); + } + + if (scanDesc == NULL) + elog(DEBUG, "ExecBeginScan: scanDesc = NULL, heap_beginscan failed."); + + + return scanDesc; } - + /* ---------------------------------------------------------------- - * ExecCloseR + * ExecCloseR * - * closes the relation and scan descriptor for a scan or sort - * node. Also closes index relations and scans for index scans. + * closes the relation and scan descriptor for a scan or sort + * node. Also closes index relations and scans for index scans. * * old comments - * closes the relation indicated in 'relID' + * closes the relation indicated in 'relID' * ---------------------------------------------------------------- */ void -ExecCloseR(Plan *node) +ExecCloseR(Plan * node) { - CommonScanState *state; - Relation relation; - HeapScanDesc scanDesc; - - /* ---------------- - * shut down the heap scan and close the heap relation - * ---------------- - */ - switch (nodeTag(node)) { - - case T_SeqScan: - state = ((SeqScan *)node)->scanstate; - break; - - case T_IndexScan: - state = ((IndexScan *)node)->scan.scanstate; - break; - - case T_Material: - state = &(((Material *)node)->matstate->csstate); - break; - - case T_Sort: - state = &(((Sort *)node)->sortstate->csstate); - break; - - case T_Agg: - state = &(((Agg *)node)->aggstate->csstate); - break; - - default: - elog(DEBUG, "ExecCloseR: not a scan, material, or sort node!"); - return; - } - - relation = state->css_currentRelation; - scanDesc = state->css_currentScanDesc; - - if (scanDesc != NULL) - heap_endscan(scanDesc); - - if (relation != NULL) - heap_close(relation); - - /* ---------------- - * if this is an index scan then we have to take care - * of the index relations as well.. - * ---------------- - */ - if (nodeTag(node) == T_IndexScan) { - IndexScan *iscan= (IndexScan *)node; - IndexScanState *indexstate; - int numIndices; - RelationPtr indexRelationDescs; - IndexScanDescPtr indexScanDescs; - int i; - - indexstate = iscan->indxstate; - numIndices = indexstate->iss_NumIndices; - indexRelationDescs = indexstate->iss_RelationDescs; - indexScanDescs = indexstate->iss_ScanDescs; - - for (i = 0; i<numIndices; i++) { - /* ---------------- - * shut down each of the scans and - * close each of the index relations - * ---------------- - */ - if (indexScanDescs[i] != NULL) - index_endscan(indexScanDescs[i]); - - if (indexRelationDescs[i] != NULL) - index_close(indexRelationDescs[i]); + CommonScanState *state; + Relation relation; + HeapScanDesc scanDesc; + + /* ---------------- + * shut down the heap scan and close the heap relation + * ---------------- + */ + switch (nodeTag(node)) + { + + case T_SeqScan: + state = ((SeqScan *) node)->scanstate; + break; + + case T_IndexScan: + state = ((IndexScan *) node)->scan.scanstate; + break; + + case T_Material: + state = &(((Material *) node)->matstate->csstate); + break; + + case T_Sort: + state = &(((Sort *) node)->sortstate->csstate); + break; + + case T_Agg: + state = &(((Agg *) node)->aggstate->csstate); + break; + + default: + elog(DEBUG, "ExecCloseR: not a scan, material, or sort node!"); + return; + } + + relation = state->css_currentRelation; + scanDesc = state->css_currentScanDesc; + + if (scanDesc != NULL) + heap_endscan(scanDesc); + + if (relation != NULL) + heap_close(relation); + + /* ---------------- + * if this is an index scan then we have to take care + * of the index relations as well.. + * ---------------- + */ + if (nodeTag(node) == T_IndexScan) + { + IndexScan *iscan = (IndexScan *) node; + IndexScanState *indexstate; + int numIndices; + RelationPtr indexRelationDescs; + IndexScanDescPtr indexScanDescs; + int i; + + indexstate = iscan->indxstate; + numIndices = indexstate->iss_NumIndices; + indexRelationDescs = indexstate->iss_RelationDescs; + indexScanDescs = indexstate->iss_ScanDescs; + + for (i = 0; i < numIndices; i++) + { + /* ---------------- + * shut down each of the scans and + * close each of the index relations + * ---------------- + */ + if (indexScanDescs[i] != NULL) + index_endscan(indexScanDescs[i]); + + if (indexRelationDescs[i] != NULL) + index_close(indexRelationDescs[i]); + } } - } } - + /* ---------------------------------------------------------------- - * ExecReScan + * ExecReScan * - * XXX this should be extended to cope with all the node types.. + * XXX this should be extended to cope with all the node types.. * - * takes the new expression context as an argument, so that - * index scans needn't have their scan keys updated separately - * - marcel 09/20/94 + * takes the new expression context as an argument, so that + * index scans needn't have their scan keys updated separately + * - marcel 09/20/94 * ---------------------------------------------------------------- */ void -ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent) +ExecReScan(Plan * node, ExprContext * exprCtxt, Plan * parent) { - switch(nodeTag(node)) { - case T_SeqScan: - ExecSeqReScan((SeqScan *) node, exprCtxt, parent); - return; - - case T_IndexScan: - ExecIndexReScan((IndexScan *) node, exprCtxt, parent); - return; + switch (nodeTag(node)) + { + case T_SeqScan: + ExecSeqReScan((SeqScan *) node, exprCtxt, parent); + return; - case T_Material: - /* the first call to ExecReScan should have no effect because - * everything is initialized properly already. the following - * calls will be handled by ExecSeqReScan() because the nodes - * below the Material node have already been materialized into - * a temp relation. - */ - return; + case T_IndexScan: + ExecIndexReScan((IndexScan *) node, exprCtxt, parent); + return; - case T_Tee: - ExecTeeReScan((Tee*) node, exprCtxt, parent); - break; + case T_Material: - default: - elog(WARN, "ExecReScan: not a seqscan or indexscan node."); - return; - } + /* + * the first call to ExecReScan should have no effect because + * everything is initialized properly already. the following + * calls will be handled by ExecSeqReScan() because the nodes + * below the Material node have already been materialized into a + * temp relation. + */ + return; + + case T_Tee: + ExecTeeReScan((Tee *) node, exprCtxt, parent); + break; + + default: + elog(WARN, "ExecReScan: not a seqscan or indexscan node."); + return; + } } - + /* ---------------------------------------------------------------- - * ExecReScanR + * ExecReScanR * - * XXX this does not do the right thing with indices yet. + * XXX this does not do the right thing with indices yet. * ---------------------------------------------------------------- */ HeapScanDesc -ExecReScanR(Relation relDesc, /* LLL relDesc unused */ - HeapScanDesc scanDesc, - ScanDirection direction, - int nkeys, /* LLL nkeys unused */ - ScanKey skeys) +ExecReScanR(Relation relDesc, /* LLL relDesc unused */ + HeapScanDesc scanDesc, + ScanDirection direction, + int nkeys, /* LLL nkeys unused */ + ScanKey skeys) { - if (scanDesc != NULL) - heap_rescan(scanDesc, /* scan desc */ - ScanDirectionIsBackward(direction), /* backward flag */ - skeys); /* scan keys */ - - return scanDesc; + if (scanDesc != NULL) + heap_rescan(scanDesc, /* scan desc */ + ScanDirectionIsBackward(direction), /* backward flag */ + skeys); /* scan keys */ + + return scanDesc; } - + /* ---------------------------------------------------------------- - * ExecMarkPos + * ExecMarkPos * - * Marks the current scan position. + * Marks the current scan position. * - * XXX Needs to be extended to include all the node types. + * XXX Needs to be extended to include all the node types. * ---------------------------------------------------------------- */ void -ExecMarkPos(Plan *node) +ExecMarkPos(Plan * node) { - switch(nodeTag(node)) { - case T_SeqScan: - ExecSeqMarkPos((SeqScan *) node); - break; - - case T_IndexScan: - ExecIndexMarkPos((IndexScan *) node); - break; - - case T_Sort: - ExecSortMarkPos((Sort *) node); - break; - - default: - /* elog(DEBUG, "ExecMarkPos: unsupported node type"); */ - break; - } - return; + switch (nodeTag(node)) + { + case T_SeqScan: + ExecSeqMarkPos((SeqScan *) node); + break; + + case T_IndexScan: + ExecIndexMarkPos((IndexScan *) node); + break; + + case T_Sort: + ExecSortMarkPos((Sort *) node); + break; + + default: + /* elog(DEBUG, "ExecMarkPos: unsupported node type"); */ + break; + } + return; } - + /* ---------------------------------------------------------------- - * ExecRestrPos + * ExecRestrPos * - * restores the scan position previously saved with ExecMarkPos() + * restores the scan position previously saved with ExecMarkPos() * ---------------------------------------------------------------- */ void -ExecRestrPos(Plan *node) +ExecRestrPos(Plan * node) { - switch(nodeTag(node)) { - case T_SeqScan: - ExecSeqRestrPos((SeqScan *) node); - return; - - case T_IndexScan: - ExecIndexRestrPos((IndexScan *) node); - return; - - case T_Sort: - ExecSortRestrPos((Sort *) node); - return; + switch (nodeTag(node)) + { + case T_SeqScan: + ExecSeqRestrPos((SeqScan *) node); + return; - default: - /* elog(DEBUG, "ExecRestrPos: node type not supported"); */ - return; - } + case T_IndexScan: + ExecIndexRestrPos((IndexScan *) node); + return; + + case T_Sort: + ExecSortRestrPos((Sort *) node); + return; + + default: + /* elog(DEBUG, "ExecRestrPos: node type not supported"); */ + return; + } } - + /* ---------------------------------------------------------------- - * ExecCreatR + * ExecCreatR * * old comments - * Creates a relation. + * Creates a relation. * - * Parameters: - * attrType -- type information on the attributes. - * accessMtd -- access methods used to access the created relation. - * relation -- optional. Either an index to the range table or - * negative number indicating a temporary relation. - * A temporary relation is assume is this field is absent. + * Parameters: + * attrType -- type information on the attributes. + * accessMtd -- access methods used to access the created relation. + * relation -- optional. Either an index to the range table or + * negative number indicating a temporary relation. + * A temporary relation is assume is this field is absent. * ---------------------------------------------------------------- */ Relation ExecCreatR(TupleDesc tupType, - Oid relationOid) + Oid relationOid) { - Relation relDesc; - - EU3_printf("ExecCreatR: %s type=%d oid=%d\n", - "entering: ", tupType, relationOid); - CXT1_printf("ExecCreatR: context is %d\n", CurrentMemoryContext); - - relDesc = NULL; - - if (relationOid == _TEMP_RELATION_ID_ ) { - /* ---------------- - * create a temporary relation - * (currently the planner always puts a _TEMP_RELATION_ID - * in the relation argument so we expect this to be the case although - * it's possible that someday we'll get the name from - * from the range table.. -cim 10/12/89) - * ---------------- - */ + Relation relDesc; + + EU3_printf("ExecCreatR: %s type=%d oid=%d\n", + "entering: ", tupType, relationOid); + CXT1_printf("ExecCreatR: context is %d\n", CurrentMemoryContext); + + relDesc = NULL; + + if (relationOid == _TEMP_RELATION_ID_) + { + /* ---------------- + * create a temporary relation + * (currently the planner always puts a _TEMP_RELATION_ID + * in the relation argument so we expect this to be the case although + * it's possible that someday we'll get the name from + * from the range table.. -cim 10/12/89) + * ---------------- + */ /* - sprintf(tempname, "temp_%d.%d", getpid(), tmpcnt++); - EU1_printf("ExecCreatR: attempting to create %s\n", tempname); + sprintf(tempname, "temp_%d.%d", getpid(), tmpcnt++); + EU1_printf("ExecCreatR: attempting to create %s\n", tempname); */ - /* heap_creatr creates a name if the argument to heap_creatr is '\0 ' */ - relDesc = heap_creatr("", - DEFAULT_SMGR, - tupType); - } else { - /* ---------------- - * use a relation from the range table - * ---------------- - */ - elog(DEBUG, "ExecCreatR: %s", - "stuff using range table id's is not functional"); - } - - if (relDesc == NULL) - elog(DEBUG, "ExecCreatR: failed to create relation."); - - EU1_printf("ExecCreatR: returning relDesc=%d\n", relDesc); - - return relDesc; + + /* + * heap_creatr creates a name if the argument to heap_creatr is + * '\0 ' + */ + relDesc = heap_creatr("", + DEFAULT_SMGR, + tupType); + } + else + { + /* ---------------- + * use a relation from the range table + * ---------------- + */ + elog(DEBUG, "ExecCreatR: %s", + "stuff using range table id's is not functional"); + } + + if (relDesc == NULL) + elog(DEBUG, "ExecCreatR: failed to create relation."); + + EU1_printf("ExecCreatR: returning relDesc=%d\n", relDesc); + + return relDesc; } - diff --git a/src/backend/executor/execFlatten.c b/src/backend/executor/execFlatten.c index c9bde2ff663..43d616712fa 100644 --- a/src/backend/executor/execFlatten.c +++ b/src/backend/executor/execFlatten.c @@ -1,29 +1,29 @@ /*------------------------------------------------------------------------- * * execFlatten.c-- - * This file handles the nodes associated with flattening sets in the - * target list of queries containing functions returning sets. + * This file handles the nodes associated with flattening sets in the + * target list of queries containing functions returning sets. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/Attic/execFlatten.c,v 1.2 1997/08/19 21:30:56 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/Attic/execFlatten.c,v 1.3 1997/09/07 04:41:12 momjian Exp $ * *------------------------------------------------------------------------- */ /* * ExecEvalIter() - - * Iterate through the all return tuples/base types from a function one - * at time (i.e. one per ExecEvalIter call). Not really needed for - * postquel functions, but for reasons of orthogonality, these nodes - * exist above pq functions as well as c functions. + * Iterate through the all return tuples/base types from a function one + * at time (i.e. one per ExecEvalIter call). Not really needed for + * postquel functions, but for reasons of orthogonality, these nodes + * exist above pq functions as well as c functions. * * ExecEvalFjoin() - - * Given N Iter nodes return a vector of all combinations of results - * one at a time (i.e. one result vector per ExecEvalFjoin call). This - * node does the actual flattening work. + * Given N Iter nodes return a vector of all combinations of results + * one at a time (i.e. one result vector per ExecEvalFjoin call). This + * node does the actual flattening work. */ #include "postgres.h" #include "nodes/primnodes.h" @@ -33,208 +33,216 @@ #include "executor/execFlatten.h" #ifdef SETS_FIXED -static bool FjoinBumpOuterNodes(TargetEntry *tlist, ExprContext *econtext, - DatumPtr results, char *nulls); +static bool +FjoinBumpOuterNodes(TargetEntry * tlist, ExprContext * econtext, + DatumPtr results, char *nulls); + #endif Datum -ExecEvalIter(Iter *iterNode, - ExprContext *econtext, - bool *resultIsNull, - bool *iterIsDone) +ExecEvalIter(Iter * iterNode, + ExprContext * econtext, + bool * resultIsNull, + bool * iterIsDone) { - Node *expression; - - expression = iterNode->iterexpr; - - /* - * Really Iter nodes are only needed for C functions, postquel function - * by their nature return 1 result at a time. For now we are only worrying - * about postquel functions, c functions will come later. - */ - return ExecEvalExpr(expression, econtext, resultIsNull, iterIsDone); + Node *expression; + + expression = iterNode->iterexpr; + + /* + * Really Iter nodes are only needed for C functions, postquel + * function by their nature return 1 result at a time. For now we are + * only worrying about postquel functions, c functions will come + * later. + */ + return ExecEvalExpr(expression, econtext, resultIsNull, iterIsDone); } void -ExecEvalFjoin(TargetEntry *tlist, - ExprContext *econtext, - bool *isNullVect, - bool *fj_isDone) +ExecEvalFjoin(TargetEntry * tlist, + ExprContext * econtext, + bool * isNullVect, + bool * fj_isDone) { #ifdef SETS_FIXED - bool isDone; - int curNode; - List *tlistP; - - Fjoin *fjNode = tlist->fjoin; - DatumPtr resVect = fjNode->fj_results; - BoolPtr alwaysDone = fjNode->fj_alwaysDone; - - if (fj_isDone) *fj_isDone = false; - /* - * For the next tuple produced by the plan, we need to re-initialize - * the Fjoin node. - */ - if (!fjNode->fj_initialized) + bool isDone; + int curNode; + List *tlistP; + + Fjoin *fjNode = tlist->fjoin; + DatumPtr resVect = fjNode->fj_results; + BoolPtr alwaysDone = fjNode->fj_alwaysDone; + + if (fj_isDone) + *fj_isDone = false; + + /* + * For the next tuple produced by the plan, we need to re-initialize + * the Fjoin node. + */ + if (!fjNode->fj_initialized) { - /* - * Initialize all of the Outer nodes - */ - curNode = 1; - foreach(tlistP, lnext(tlist)) + + /* + * Initialize all of the Outer nodes + */ + curNode = 1; + foreach(tlistP, lnext(tlist)) { - TargetEntry *tle = lfirst(tlistP); - - resVect[curNode] = ExecEvalIter((Iter*)tle->expr, - econtext, - &isNullVect[curNode], - &isDone); - if (isDone) - isNullVect[curNode] = alwaysDone[curNode] = true; - else - alwaysDone[curNode] = false; - - curNode++; + TargetEntry *tle = lfirst(tlistP); + + resVect[curNode] = ExecEvalIter((Iter *) tle->expr, + econtext, + &isNullVect[curNode], + &isDone); + if (isDone) + isNullVect[curNode] = alwaysDone[curNode] = true; + else + alwaysDone[curNode] = false; + + curNode++; } - - /* - * Initialize the inner node - */ - resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr, - econtext, - &isNullVect[0], - &isDone); - if (isDone) - isNullVect[0] = alwaysDone[0] = true; - else - alwaysDone[0] = false; - - /* - * Mark the Fjoin as initialized now. - */ - fjNode->fj_initialized = TRUE; - - /* - * If the inner node is always done, then we are done for now - */ - if (isDone) - return; + + /* + * Initialize the inner node + */ + resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr, + econtext, + &isNullVect[0], + &isDone); + if (isDone) + isNullVect[0] = alwaysDone[0] = true; + else + alwaysDone[0] = false; + + /* + * Mark the Fjoin as initialized now. + */ + fjNode->fj_initialized = TRUE; + + /* + * If the inner node is always done, then we are done for now + */ + if (isDone) + return; } - else + else { - /* - * If we're already initialized, all we need to do is get the - * next inner result and pair it up with the existing outer node - * result vector. Watch out for the degenerate case, where the - * inner node never returns results. - */ - - /* - * Fill in nulls for every function that is always done. - */ - for (curNode=fjNode->fj_nNodes-1; curNode >= 0; curNode--) - isNullVect[curNode] = alwaysDone[curNode]; - - if (alwaysDone[0] == true) + + /* + * If we're already initialized, all we need to do is get the next + * inner result and pair it up with the existing outer node result + * vector. Watch out for the degenerate case, where the inner + * node never returns results. + */ + + /* + * Fill in nulls for every function that is always done. + */ + for (curNode = fjNode->fj_nNodes - 1; curNode >= 0; curNode--) + isNullVect[curNode] = alwaysDone[curNode]; + + if (alwaysDone[0] == true) { - *fj_isDone = FjoinBumpOuterNodes(tlist, - econtext, - resVect, - isNullVect); - return; + *fj_isDone = FjoinBumpOuterNodes(tlist, + econtext, + resVect, + isNullVect); + return; } - else - resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr, - econtext, - &isNullVect[0], - &isDone); + else + resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr, + econtext, + &isNullVect[0], + &isDone); } - - /* - * if the inner node is done - */ - if (isDone) + + /* + * if the inner node is done + */ + if (isDone) { - *fj_isDone = FjoinBumpOuterNodes(tlist, - econtext, - resVect, - isNullVect); - if (*fj_isDone) - return; - - resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr, - econtext, - &isNullVect[0], - &isDone); - + *fj_isDone = FjoinBumpOuterNodes(tlist, + econtext, + resVect, + isNullVect); + if (*fj_isDone) + return; + + resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr, + econtext, + &isNullVect[0], + &isDone); + } #endif - return; + return; } #ifdef SETS_FIXED -static bool -FjoinBumpOuterNodes(TargetEntry *tlist, - ExprContext *econtext, - DatumPtr results, - char *nulls) +static bool +FjoinBumpOuterNodes(TargetEntry * tlist, + ExprContext * econtext, + DatumPtr results, + char *nulls) { - bool funcIsDone = true; - Fjoin *fjNode = tlist->fjoin; - char *alwaysDone = fjNode->fj_alwaysDone; - List *outerList = lnext(tlist); - List *trailers = lnext(tlist); - int trailNode = 1; - int curNode = 1; - - /* - * Run through list of functions until we get to one that isn't yet - * done returning values. Watch out for funcs that are always done. - */ - while ((funcIsDone == true) && (outerList != NIL)) + bool funcIsDone = true; + Fjoin *fjNode = tlist->fjoin; + char *alwaysDone = fjNode->fj_alwaysDone; + List *outerList = lnext(tlist); + List *trailers = lnext(tlist); + int trailNode = 1; + int curNode = 1; + + /* + * Run through list of functions until we get to one that isn't yet + * done returning values. Watch out for funcs that are always done. + */ + while ((funcIsDone == true) && (outerList != NIL)) { - TargetEntry *tle = lfirst(outerList); - - if (alwaysDone[curNode] == true) - nulls[curNode] = 'n'; - else - results[curNode] = ExecEvalIter((Iter)tle->expr, - econtext, - &nulls[curNode], - &funcIsDone); - curNode++; - outerList = lnext(outerList); + TargetEntry *tle = lfirst(outerList); + + if (alwaysDone[curNode] == true) + nulls[curNode] = 'n'; + else + results[curNode] = ExecEvalIter((Iter) tle->expr, + econtext, + &nulls[curNode], + &funcIsDone); + curNode++; + outerList = lnext(outerList); } - - /* - * If every function is done, then we are done flattening. - * Mark the Fjoin node unitialized, it is time to get the - * next tuple from the plan and redo all of the flattening. - */ - if (funcIsDone) + + /* + * If every function is done, then we are done flattening. Mark the + * Fjoin node unitialized, it is time to get the next tuple from the + * plan and redo all of the flattening. + */ + if (funcIsDone) { - set_fj_initialized(fjNode, false); - return (true); + set_fj_initialized(fjNode, false); + return (true); } - - /* - * We found a function that wasn't done. Now re-run every function - * before it. As usual watch out for functions that are always done. - */ - trailNode = 1; - while (trailNode != curNode-1) + + /* + * We found a function that wasn't done. Now re-run every function + * before it. As usual watch out for functions that are always done. + */ + trailNode = 1; + while (trailNode != curNode - 1) { - TargetEntry *tle = lfirst(trailers); - - if (alwaysDone[trailNode] != true) - results[trailNode] = ExecEvalIter((Iter)tle->expr, - econtext, - &nulls[trailNode], - &funcIsDone); - trailNode++; - trailers = lnext(trailers); + TargetEntry *tle = lfirst(trailers); + + if (alwaysDone[trailNode] != true) + results[trailNode] = ExecEvalIter((Iter) tle->expr, + econtext, + &nulls[trailNode], + &funcIsDone); + trailNode++; + trailers = lnext(trailers); } - return false; + return false; } + #endif diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c index 8779647a113..3ad41bd393f 100644 --- a/src/backend/executor/execJunk.c +++ b/src/backend/executor/execJunk.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * junk.c-- - * Junk attribute support stuff.... + * Junk attribute support stuff.... * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execJunk.c,v 1.5 1997/08/26 23:31:37 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execJunk.c,v 1.6 1997/09/07 04:41:14 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -20,37 +20,37 @@ #include "access/heapam.h" #include "executor/executor.h" #include "nodes/relation.h" -#include "optimizer/tlist.h" /* for MakeTLE */ +#include "optimizer/tlist.h" /* for MakeTLE */ /*------------------------------------------------------------------------- - * XXX this stuff should be rewritten to take advantage - * of ExecProject() and the ProjectionInfo node. - * -cim 6/3/91 - * + * XXX this stuff should be rewritten to take advantage + * of ExecProject() and the ProjectionInfo node. + * -cim 6/3/91 + * * An attribute of a tuple living inside the executor, can be * either a normal attribute or a "junk" attribute. "junk" attributes * never make it out of the executor, i.e. they are never printed, * returned or stored in disk. Their only purpose in life is to * store some information useful only to the executor, mainly the values * of some system attributes like "ctid" or rule locks. - * + * * The general idea is the following: A target list consists of a list of * Resdom nodes & expression pairs. Each Resdom node has an attribute * called 'resjunk'. If the value of this attribute is 1 then the * corresponding attribute is a "junk" attribute. - * + * * When we initialize a plan we call 'ExecInitJunkFilter' to create * and store the appropriate information in the 'es_junkFilter' attribute of * EState. - * + * * We then execute the plan ignoring the "resjunk" attributes. - * + * * Finally, when at the top level we get back a tuple, we can call * 'ExecGetJunkAttribute' to retrieve the value of the junk attributes we * are interested in, and 'ExecRemoveJunk' to remove all the junk attributes * from a tuple. This new "clean" tuple is then printed, replaced, deleted * or inserted. - * + * *------------------------------------------------------------------------- */ @@ -60,174 +60,196 @@ * Initialize the Junk filter. *------------------------------------------------------------------------- */ -JunkFilter * -ExecInitJunkFilter(List *targetList) +JunkFilter * +ExecInitJunkFilter(List * targetList) { - JunkFilter *junkfilter; - List *cleanTargetList; - int len, cleanLength; - TupleDesc tupType, cleanTupType; - List *t; - TargetEntry *tle; - Resdom *resdom, *cleanResdom; - int resjunk; - AttrNumber cleanResno; - AttrNumber *cleanMap; - Size size; - Node *expr; - - /* --------------------- - * First find the "clean" target list, i.e. all the entries - * in the original target list which have a zero 'resjunk' - * NOTE: make copy of the Resdom nodes, because we have - * to change the 'resno's... - * --------------------- - */ - cleanTargetList = NIL; - cleanResno = 1; - - foreach (t, targetList) { - TargetEntry *rtarget = lfirst(t); - if (rtarget->resdom != NULL) { - resdom = rtarget->resdom; - expr = rtarget->expr; - resjunk = resdom->resjunk; - if (resjunk == 0) { - /* - * make a copy of the resdom node, changing its resno. - */ - cleanResdom = (Resdom *) copyObject(resdom); - cleanResdom->resno = cleanResno; - cleanResno ++; - /* - * create a new target list entry - */ - tle = makeNode(TargetEntry); - tle->resdom = cleanResdom; - tle->expr = expr; - cleanTargetList = lappend(cleanTargetList, tle); - } - } - else { + JunkFilter *junkfilter; + List *cleanTargetList; + int len, + cleanLength; + TupleDesc tupType, + cleanTupType; + List *t; + TargetEntry *tle; + Resdom *resdom, + *cleanResdom; + int resjunk; + AttrNumber cleanResno; + AttrNumber *cleanMap; + Size size; + Node *expr; + + /* --------------------- + * First find the "clean" target list, i.e. all the entries + * in the original target list which have a zero 'resjunk' + * NOTE: make copy of the Resdom nodes, because we have + * to change the 'resno's... + * --------------------- + */ + cleanTargetList = NIL; + cleanResno = 1; + + foreach(t, targetList) + { + TargetEntry *rtarget = lfirst(t); + + if (rtarget->resdom != NULL) + { + resdom = rtarget->resdom; + expr = rtarget->expr; + resjunk = resdom->resjunk; + if (resjunk == 0) + { + + /* + * make a copy of the resdom node, changing its resno. + */ + cleanResdom = (Resdom *) copyObject(resdom); + cleanResdom->resno = cleanResno; + cleanResno++; + + /* + * create a new target list entry + */ + tle = makeNode(TargetEntry); + tle->resdom = cleanResdom; + tle->expr = expr; + cleanTargetList = lappend(cleanTargetList, tle); + } + } + else + { #ifdef SETS_FIXED - List *fjListP; - Fjoin *cleanFjoin; - List *cleanFjList; - List *fjList = lfirst(t); - Fjoin *fjNode = (Fjoin *)tl_node(fjList); - - cleanFjoin = (Fjoin)copyObject((Node) fjNode); - cleanFjList = lcons(cleanFjoin, NIL); - - resdom = (Resdom) lfirst(get_fj_innerNode(fjNode)); - expr = lsecond(get_fj_innerNode(fjNode)); - cleanResdom = (Resdom) copyObject((Node) resdom); - set_resno(cleanResdom, cleanResno); - cleanResno++; - tle = (List) MakeTLE(cleanResdom, (Expr) expr); - set_fj_innerNode(cleanFjoin, tle); - - foreach(fjListP, lnext(fjList)) { - TargetEntry *tle = lfirst(fjListP); - - resdom = tle->resdom; - expr = tle->expr; - cleanResdom = (Resdom*) copyObject((Node) resdom); - cleanResno++; - cleanResdom->Resno = cleanResno; - /* - * create a new target list entry - */ - tle = (List) MakeTLE(cleanResdom, (Expr) expr); - cleanFjList = lappend(cleanFjList, tle); - } - lappend(cleanTargetList, cleanFjList); + List *fjListP; + Fjoin *cleanFjoin; + List *cleanFjList; + List *fjList = lfirst(t); + Fjoin *fjNode = (Fjoin *) tl_node(fjList); + + cleanFjoin = (Fjoin) copyObject((Node) fjNode); + cleanFjList = lcons(cleanFjoin, NIL); + + resdom = (Resdom) lfirst(get_fj_innerNode(fjNode)); + expr = lsecond(get_fj_innerNode(fjNode)); + cleanResdom = (Resdom) copyObject((Node) resdom); + set_resno(cleanResdom, cleanResno); + cleanResno++; + tle = (List) MakeTLE(cleanResdom, (Expr) expr); + set_fj_innerNode(cleanFjoin, tle); + + foreach(fjListP, lnext(fjList)) + { + TargetEntry *tle = lfirst(fjListP); + + resdom = tle->resdom; + expr = tle->expr; + cleanResdom = (Resdom *) copyObject((Node) resdom); + cleanResno++; + cleanResdom->Resno = cleanResno; + + /* + * create a new target list entry + */ + tle = (List) MakeTLE(cleanResdom, (Expr) expr); + cleanFjList = lappend(cleanFjList, tle); + } + lappend(cleanTargetList, cleanFjList); #endif - } - } - - /* --------------------- - * Now calculate the tuple types for the original and the clean tuple - * - * XXX ExecTypeFromTL should be used sparingly. Don't we already - * have the tupType corresponding to the targetlist we are passed? - * -cim 5/31/91 - * --------------------- - */ - tupType = (TupleDesc) ExecTypeFromTL(targetList); - cleanTupType = (TupleDesc) ExecTypeFromTL(cleanTargetList); - - len = ExecTargetListLength(targetList); - cleanLength = ExecTargetListLength(cleanTargetList); - - /* --------------------- - * Now calculate the "map" between the original tuples attributes - * and the "clean" tuple's attributes. - * - * The "map" is an array of "cleanLength" attribute numbers, i.e. - * one entry for every attribute of the "clean" tuple. - * The value of this entry is the attribute number of the corresponding - * attribute of the "original" tuple. - * --------------------- - */ - if (cleanLength > 0) { - size = cleanLength * sizeof(AttrNumber); - cleanMap = (AttrNumber*) palloc(size); - cleanResno = 1; - foreach (t, targetList) { - TargetEntry *tle = lfirst(t); - if (tle->resdom != NULL) { - resdom = tle->resdom; - expr = tle->expr; - resjunk = resdom->resjunk; - if (resjunk == 0) { - cleanMap[cleanResno-1] = resdom->resno; - cleanResno ++; } - } else { + } + + /* --------------------- + * Now calculate the tuple types for the original and the clean tuple + * + * XXX ExecTypeFromTL should be used sparingly. Don't we already + * have the tupType corresponding to the targetlist we are passed? + * -cim 5/31/91 + * --------------------- + */ + tupType = (TupleDesc) ExecTypeFromTL(targetList); + cleanTupType = (TupleDesc) ExecTypeFromTL(cleanTargetList); + + len = ExecTargetListLength(targetList); + cleanLength = ExecTargetListLength(cleanTargetList); + + /* --------------------- + * Now calculate the "map" between the original tuples attributes + * and the "clean" tuple's attributes. + * + * The "map" is an array of "cleanLength" attribute numbers, i.e. + * one entry for every attribute of the "clean" tuple. + * The value of this entry is the attribute number of the corresponding + * attribute of the "original" tuple. + * --------------------- + */ + if (cleanLength > 0) + { + size = cleanLength * sizeof(AttrNumber); + cleanMap = (AttrNumber *) palloc(size); + cleanResno = 1; + foreach(t, targetList) + { + TargetEntry *tle = lfirst(t); + + if (tle->resdom != NULL) + { + resdom = tle->resdom; + expr = tle->expr; + resjunk = resdom->resjunk; + if (resjunk == 0) + { + cleanMap[cleanResno - 1] = resdom->resno; + cleanResno++; + } + } + else + { #ifdef SETS_FIXED - List fjListP; - List fjList = lfirst(t); - Fjoin fjNode = (Fjoin)lfirst(fjList); + List fjListP; + List fjList = lfirst(t); + Fjoin fjNode = (Fjoin) lfirst(fjList); - /* what the hell is this????? */ - resdom = (Resdom) lfirst(get_fj_innerNode(fjNode)); + /* what the hell is this????? */ + resdom = (Resdom) lfirst(get_fj_innerNode(fjNode)); #endif - cleanMap[cleanResno-1] = tle->resdom->resno; - cleanResno++; + cleanMap[cleanResno - 1] = tle->resdom->resno; + cleanResno++; #ifdef SETS_FIXED - foreach(fjListP, lnext(fjList)) { - TargetEntry *tle = lfirst(fjListP); + foreach(fjListP, lnext(fjList)) + { + TargetEntry *tle = lfirst(fjListP); - resdom = tle->resdom; - cleanMap[cleanResno-1] = resdom->resno; - cleanResno++; - } + resdom = tle->resdom; + cleanMap[cleanResno - 1] = resdom->resno; + cleanResno++; + } #endif - } + } + } + } + else + { + cleanMap = NULL; } - } else { - cleanMap = NULL; - } - - /* --------------------- - * Finally create and initialize the JunkFilter. - * --------------------- - */ - junkfilter = makeNode(JunkFilter); - - junkfilter->jf_targetList = targetList; - junkfilter->jf_length = len; - junkfilter->jf_tupType = tupType; - junkfilter->jf_cleanTargetList = cleanTargetList; - junkfilter->jf_cleanLength = cleanLength; - junkfilter->jf_cleanTupType = cleanTupType; - junkfilter->jf_cleanMap = cleanMap; - - return(junkfilter); - + + /* --------------------- + * Finally create and initialize the JunkFilter. + * --------------------- + */ + junkfilter = makeNode(JunkFilter); + + junkfilter->jf_targetList = targetList; + junkfilter->jf_length = len; + junkfilter->jf_tupType = tupType; + junkfilter->jf_cleanTargetList = cleanTargetList; + junkfilter->jf_cleanLength = cleanLength; + junkfilter->jf_cleanTupType = cleanTupType; + junkfilter->jf_cleanMap = cleanMap; + + return (junkfilter); + } /*------------------------------------------------------------------------- @@ -242,57 +264,61 @@ ExecInitJunkFilter(List *targetList) *------------------------------------------------------------------------- */ bool -ExecGetJunkAttribute(JunkFilter *junkfilter, - TupleTableSlot *slot, - char *attrName, - Datum *value, - bool *isNull) +ExecGetJunkAttribute(JunkFilter * junkfilter, + TupleTableSlot * slot, + char *attrName, + Datum * value, + bool * isNull) { - List *targetList; - List *t; - Resdom *resdom; - AttrNumber resno; - char *resname; - int resjunk; - TupleDesc tupType; - HeapTuple tuple; - - /* --------------------- - * first look in the junkfilter's target list for - * an attribute with the given name - * --------------------- - */ - resno = InvalidAttrNumber; - targetList = junkfilter->jf_targetList; - - foreach (t, targetList) { - TargetEntry *tle = lfirst(t); - resdom = tle->resdom; - resname = resdom->resname; - resjunk = resdom->resjunk; - if (resjunk != 0 && (strcmp(resname, attrName) == 0)) { - /* We found it ! */ - resno = resdom->resno; - break; + List *targetList; + List *t; + Resdom *resdom; + AttrNumber resno; + char *resname; + int resjunk; + TupleDesc tupType; + HeapTuple tuple; + + /* --------------------- + * first look in the junkfilter's target list for + * an attribute with the given name + * --------------------- + */ + resno = InvalidAttrNumber; + targetList = junkfilter->jf_targetList; + + foreach(t, targetList) + { + TargetEntry *tle = lfirst(t); + + resdom = tle->resdom; + resname = resdom->resname; + resjunk = resdom->resjunk; + if (resjunk != 0 && (strcmp(resname, attrName) == 0)) + { + /* We found it ! */ + resno = resdom->resno; + break; + } + } + + if (resno == InvalidAttrNumber) + { + /* Ooops! We couldn't find this attribute... */ + return (false); } - } - - if (resno == InvalidAttrNumber) { - /* Ooops! We couldn't find this attribute... */ - return(false); - } - - /* --------------------- - * Now extract the attribute value from the tuple. - * --------------------- - */ - tuple = slot->val; - tupType = (TupleDesc) junkfilter->jf_tupType; - - *value = (Datum) - heap_getattr(tuple, InvalidBuffer, resno, tupType, isNull); - - return true; + + /* --------------------- + * Now extract the attribute value from the tuple. + * --------------------- + */ + tuple = slot->val; + tupType = (TupleDesc) junkfilter->jf_tupType; + + *value = (Datum) + heap_getattr(tuple, InvalidBuffer, resno, tupType, isNull); + + return true; } /*------------------------------------------------------------------------- @@ -302,94 +328,98 @@ ExecGetJunkAttribute(JunkFilter *junkfilter, *------------------------------------------------------------------------- */ HeapTuple -ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot) +ExecRemoveJunk(JunkFilter * junkfilter, TupleTableSlot * slot) { - HeapTuple tuple; - HeapTuple cleanTuple; - AttrNumber *cleanMap; - TupleDesc cleanTupType; - TupleDesc tupType; - int cleanLength; - bool isNull; - int i; - Size size; - Datum *values; - char *nulls; - Datum values_array[64]; - char nulls_array[64]; - - /* ---------------- - * get info from the slot and the junk filter - * ---------------- - */ - tuple = slot->val; - - tupType = (TupleDesc) junkfilter->jf_tupType; - cleanTupType = (TupleDesc) junkfilter->jf_cleanTupType; - cleanLength = junkfilter->jf_cleanLength; - cleanMap = junkfilter->jf_cleanMap; - - /* --------------------- - * Handle the trivial case first. - * --------------------- - */ - if (cleanLength == 0) - return (HeapTuple) NULL; - - /* --------------------- - * Create the arrays that will hold the attribute values - * and the null information for the new "clean" tuple. - * - * Note: we use memory on the stack to optimize things when - * we are dealing with a small number of tuples. - * for large tuples we just use palloc. - * --------------------- - */ - if (cleanLength > 64) { - size = cleanLength * sizeof(Datum); - values = (Datum *) palloc(size); - - size = cleanLength * sizeof(char); - nulls = (char *) palloc(size); - } else { - values = values_array; - nulls = nulls_array; - } - - /* --------------------- - * Exctract one by one all the values of the "clean" tuple. - * --------------------- - */ - for (i=0; i<cleanLength; i++) { - Datum d = (Datum) - heap_getattr(tuple, InvalidBuffer, cleanMap[i], tupType, &isNull); - - values[i] = d; - - if (isNull) - nulls[i] = 'n'; + HeapTuple tuple; + HeapTuple cleanTuple; + AttrNumber *cleanMap; + TupleDesc cleanTupType; + TupleDesc tupType; + int cleanLength; + bool isNull; + int i; + Size size; + Datum *values; + char *nulls; + Datum values_array[64]; + char nulls_array[64]; + + /* ---------------- + * get info from the slot and the junk filter + * ---------------- + */ + tuple = slot->val; + + tupType = (TupleDesc) junkfilter->jf_tupType; + cleanTupType = (TupleDesc) junkfilter->jf_cleanTupType; + cleanLength = junkfilter->jf_cleanLength; + cleanMap = junkfilter->jf_cleanMap; + + /* --------------------- + * Handle the trivial case first. + * --------------------- + */ + if (cleanLength == 0) + return (HeapTuple) NULL; + + /* --------------------- + * Create the arrays that will hold the attribute values + * and the null information for the new "clean" tuple. + * + * Note: we use memory on the stack to optimize things when + * we are dealing with a small number of tuples. + * for large tuples we just use palloc. + * --------------------- + */ + if (cleanLength > 64) + { + size = cleanLength * sizeof(Datum); + values = (Datum *) palloc(size); + + size = cleanLength * sizeof(char); + nulls = (char *) palloc(size); + } else - nulls[i] = ' '; - } - - /* --------------------- - * Now form the new tuple. - * --------------------- - */ - cleanTuple = heap_formtuple(cleanTupType, - values, - nulls); - - /* --------------------- - * We are done. Free any space allocated for 'values' and 'nulls' - * and return the new tuple. - * --------------------- - */ - if (cleanLength > 64) { - pfree(values); - pfree(nulls); - } - - return(cleanTuple); -} + { + values = values_array; + nulls = nulls_array; + } + + /* --------------------- + * Exctract one by one all the values of the "clean" tuple. + * --------------------- + */ + for (i = 0; i < cleanLength; i++) + { + Datum d = (Datum) + heap_getattr(tuple, InvalidBuffer, cleanMap[i], tupType, &isNull); + values[i] = d; + + if (isNull) + nulls[i] = 'n'; + else + nulls[i] = ' '; + } + + /* --------------------- + * Now form the new tuple. + * --------------------- + */ + cleanTuple = heap_formtuple(cleanTupType, + values, + nulls); + + /* --------------------- + * We are done. Free any space allocated for 'values' and 'nulls' + * and return the new tuple. + * --------------------- + */ + if (cleanLength > 64) + { + pfree(values); + pfree(nulls); + } + + return (cleanTuple); +} diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 48bf84ba095..2bf0edaf35e 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1,32 +1,32 @@ /*------------------------------------------------------------------------- * * execMain.c-- - * top level executor interface routines + * top level executor interface routines * * INTERFACE ROUTINES - * ExecutorStart() - * ExecutorRun() - * ExecutorEnd() + * ExecutorStart() + * ExecutorRun() + * ExecutorEnd() * - * The old ExecutorMain() has been replaced by ExecutorStart(), - * ExecutorRun() and ExecutorEnd() + * The old ExecutorMain() has been replaced by ExecutorStart(), + * ExecutorRun() and ExecutorEnd() + * + * These three procedures are the external interfaces to the executor. + * In each case, the query descriptor and the execution state is required + * as arguments + * + * ExecutorStart() must be called at the beginning of any execution of any + * query plan and ExecutorEnd() should always be called at the end of + * execution of a plan. + * + * ExecutorRun accepts 'feature' and 'count' arguments that specify whether + * the plan is to be executed forwards, backwards, and for how many tuples. * - * These three procedures are the external interfaces to the executor. - * In each case, the query descriptor and the execution state is required - * as arguments - * - * ExecutorStart() must be called at the beginning of any execution of any - * query plan and ExecutorEnd() should always be called at the end of - * execution of a plan. - * - * ExecutorRun accepts 'feature' and 'count' arguments that specify whether - * the plan is to be executed forwards, backwards, and for how many tuples. - * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.22 1997/09/04 13:22:36 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.23 1997/09/07 04:41:18 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -42,7 +42,7 @@ #include "utils/palloc.h" #include "utils/acl.h" #include "utils/syscache.h" -#include "parser/parsetree.h" /* rt_fetch() */ +#include "parser/parsetree.h" /* rt_fetch() */ #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/smgr.h" @@ -56,28 +56,35 @@ /* decls for local routines only used within this module */ -static void ExecCheckPerms(CmdType operation, int resultRelation, List *rangeTable, - Query *parseTree); -static TupleDesc InitPlan(CmdType operation, Query *parseTree, - Plan *plan, EState *estate); -static void EndPlan(Plan *plan, EState *estate); -static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan, - Query *parseTree, CmdType operation, - int numberTuples, ScanDirection direction, - void (*printfunc)()); -static void ExecRetrieve(TupleTableSlot *slot, void (*printfunc)(), - EState *estate); -static void ExecAppend(TupleTableSlot *slot,ItemPointer tupleid, - EState *estate); -static void ExecDelete(TupleTableSlot *slot, ItemPointer tupleid, - EState *estate); -static void ExecReplace(TupleTableSlot *slot, ItemPointer tupleid, - EState *estate, Query *parseTree); +static void +ExecCheckPerms(CmdType operation, int resultRelation, List * rangeTable, + Query * parseTree); +static TupleDesc +InitPlan(CmdType operation, Query * parseTree, + Plan * plan, EState * estate); +static void EndPlan(Plan * plan, EState * estate); +static TupleTableSlot * +ExecutePlan(EState * estate, Plan * plan, + Query * parseTree, CmdType operation, + int numberTuples, ScanDirection direction, + void (*printfunc) ()); +static void ExecRetrieve(TupleTableSlot * slot, void (*printfunc) (), + EState * estate); +static void +ExecAppend(TupleTableSlot * slot, ItemPointer tupleid, + EState * estate); +static void +ExecDelete(TupleTableSlot * slot, ItemPointer tupleid, + EState * estate); +static void +ExecReplace(TupleTableSlot * slot, ItemPointer tupleid, + EState * estate, Query * parseTree); /* end of local decls */ #ifdef QUERY_LIMIT -static int queryLimit = ALL_TUPLES; +static int queryLimit = ALL_TUPLES; + #undef ALL_TUPLES #define ALL_TUPLES queryLimit @@ -85,574 +92,605 @@ static int queryLimit = ALL_TUPLES; int ExecutorLimit(int limit) { - return queryLimit = limit; + return queryLimit = limit; } + #endif #endif /* ---------------------------------------------------------------- - * ExecutorStart - * - * This routine must be called at the beginning of any execution of any - * query plan - * - * returns (AttrInfo*) which describes the attributes of the tuples to - * be returned by the query. + * ExecutorStart + * + * This routine must be called at the beginning of any execution of any + * query plan + * + * returns (AttrInfo*) which describes the attributes of the tuples to + * be returned by the query. * * ---------------------------------------------------------------- */ TupleDesc -ExecutorStart(QueryDesc *queryDesc, EState *estate) +ExecutorStart(QueryDesc * queryDesc, EState * estate) { - TupleDesc result; - - /* sanity checks */ - Assert(queryDesc!=NULL); - - result = InitPlan(queryDesc->operation, - queryDesc->parsetree, - queryDesc->plantree, - estate); - - /* reset buffer refcount. the current refcounts - * are saved and will be restored when ExecutorEnd is called - * - * this makes sure that when ExecutorRun's are - * called recursively as for postquel functions, - * the buffers pinned by one ExecutorRun will not be - * unpinned by another ExecutorRun. - */ - BufferRefCountReset(estate->es_refcount); - - return result; + TupleDesc result; + + /* sanity checks */ + Assert(queryDesc != NULL); + + result = InitPlan(queryDesc->operation, + queryDesc->parsetree, + queryDesc->plantree, + estate); + + /* + * reset buffer refcount. the current refcounts are saved and will be + * restored when ExecutorEnd is called + * + * this makes sure that when ExecutorRun's are called recursively as for + * postquel functions, the buffers pinned by one ExecutorRun will not + * be unpinned by another ExecutorRun. + */ + BufferRefCountReset(estate->es_refcount); + + return result; } /* ---------------------------------------------------------------- - * ExecutorRun - * - * This is the main routine of the executor module. It accepts - * the query descriptor from the traffic cop and executes the - * query plan. - * - * ExecutorStart must have been called already. + * ExecutorRun + * + * This is the main routine of the executor module. It accepts + * the query descriptor from the traffic cop and executes the + * query plan. + * + * ExecutorStart must have been called already. * - * the different features supported are: - * EXEC_RUN: retrieve all tuples in the forward direction - * EXEC_FOR: retrieve 'count' number of tuples in the forward dir - * EXEC_BACK: retrieve 'count' number of tuples in the backward dir - * EXEC_RETONE: return one tuple but don't 'retrieve' it - * used in postquel function processing + * the different features supported are: + * EXEC_RUN: retrieve all tuples in the forward direction + * EXEC_FOR: retrieve 'count' number of tuples in the forward dir + * EXEC_BACK: retrieve 'count' number of tuples in the backward dir + * EXEC_RETONE: return one tuple but don't 'retrieve' it + * used in postquel function processing * * * ---------------------------------------------------------------- */ -TupleTableSlot* -ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count) +TupleTableSlot * +ExecutorRun(QueryDesc * queryDesc, EState * estate, int feature, int count) { - CmdType operation; - Query *parseTree; - Plan *plan; - TupleTableSlot *result; - CommandDest dest; - void (*destination)(); - - /* ---------------- - * sanity checks - * ---------------- - */ - Assert(queryDesc!=NULL); - - /* ---------------- - * extract information from the query descriptor - * and the query feature. - * ---------------- - */ - operation = queryDesc->operation; - parseTree = queryDesc->parsetree; - plan = queryDesc->plantree; - dest = queryDesc->dest; - destination = (void (*)()) DestToFunction(dest); - estate->es_processed = 0; - estate->es_lastoid = InvalidOid; + CmdType operation; + Query *parseTree; + Plan *plan; + TupleTableSlot *result; + CommandDest dest; + void (*destination) (); -#if 0 - /* - * It doesn't work in common case (i.g. if function has a aggregate). - * Now we store parameter values before ExecutorStart. - vadim 01/22/97 - */ -#ifdef INDEXSCAN_PATCH - /* - * If the plan is an index scan and some of the scan key are - * function arguments rescan the indices after the parameter - * values have been stored in the execution state. DZ - 27-8-1996 - */ - if ((nodeTag(plan) == T_IndexScan) && - (((IndexScan *)plan)->indxstate->iss_RuntimeKeyInfo != NULL)) { - ExprContext *econtext; - econtext = ((IndexScan *)plan)->scan.scanstate->cstate.cs_ExprContext; - ExecIndexReScan((IndexScan *)plan, econtext, plan); - } -#endif -#endif - - switch(feature) { - - case EXEC_RUN: - result = ExecutePlan(estate, - plan, - parseTree, - operation, - ALL_TUPLES, - ForwardScanDirection, - destination); - break; - case EXEC_FOR: - result = ExecutePlan(estate, - plan, - parseTree, - operation, - count, - ForwardScanDirection, - destination); - break; - /* ---------------- - * retrieve next n "backward" tuples + * sanity checks * ---------------- */ - case EXEC_BACK: - result = ExecutePlan(estate, - plan, - parseTree, - operation, - count, - BackwardScanDirection, - destination); - break; - + Assert(queryDesc != NULL); + /* ---------------- - * return one tuple but don't "retrieve" it. - * (this is used by the rule manager..) -cim 9/14/89 + * extract information from the query descriptor + * and the query feature. * ---------------- */ - case EXEC_RETONE: - result = ExecutePlan(estate, - plan, - parseTree, - operation, - ONE_TUPLE, - ForwardScanDirection, - destination); - break; - default: - result = NULL; - elog(DEBUG, "ExecutorRun: Unknown feature %d", feature); - break; - } + operation = queryDesc->operation; + parseTree = queryDesc->parsetree; + plan = queryDesc->plantree; + dest = queryDesc->dest; + destination = (void (*) ()) DestToFunction(dest); + estate->es_processed = 0; + estate->es_lastoid = InvalidOid; + +#if 0 + + /* + * It doesn't work in common case (i.g. if function has a aggregate). + * Now we store parameter values before ExecutorStart. - vadim + * 01/22/97 + */ +#ifdef INDEXSCAN_PATCH + + /* + * If the plan is an index scan and some of the scan key are function + * arguments rescan the indices after the parameter values have been + * stored in the execution state. DZ - 27-8-1996 + */ + if ((nodeTag(plan) == T_IndexScan) && + (((IndexScan *) plan)->indxstate->iss_RuntimeKeyInfo != NULL)) + { + ExprContext *econtext; + + econtext = ((IndexScan *) plan)->scan.scanstate->cstate.cs_ExprContext; + ExecIndexReScan((IndexScan *) plan, econtext, plan); + } +#endif +#endif + + switch (feature) + { + + case EXEC_RUN: + result = ExecutePlan(estate, + plan, + parseTree, + operation, + ALL_TUPLES, + ForwardScanDirection, + destination); + break; + case EXEC_FOR: + result = ExecutePlan(estate, + plan, + parseTree, + operation, + count, + ForwardScanDirection, + destination); + break; - return result; + /* ---------------- + * retrieve next n "backward" tuples + * ---------------- + */ + case EXEC_BACK: + result = ExecutePlan(estate, + plan, + parseTree, + operation, + count, + BackwardScanDirection, + destination); + break; + + /* ---------------- + * return one tuple but don't "retrieve" it. + * (this is used by the rule manager..) -cim 9/14/89 + * ---------------- + */ + case EXEC_RETONE: + result = ExecutePlan(estate, + plan, + parseTree, + operation, + ONE_TUPLE, + ForwardScanDirection, + destination); + break; + default: + result = NULL; + elog(DEBUG, "ExecutorRun: Unknown feature %d", feature); + break; + } + + return result; } /* ---------------------------------------------------------------- - * ExecutorEnd - * - * This routine must be called at the end of any execution of any - * query plan - * - * returns (AttrInfo*) which describes the attributes of the tuples to - * be returned by the query. + * ExecutorEnd + * + * This routine must be called at the end of any execution of any + * query plan + * + * returns (AttrInfo*) which describes the attributes of the tuples to + * be returned by the query. * * ---------------------------------------------------------------- */ void -ExecutorEnd(QueryDesc *queryDesc, EState *estate) +ExecutorEnd(QueryDesc * queryDesc, EState * estate) { - /* sanity checks */ - Assert(queryDesc!=NULL); + /* sanity checks */ + Assert(queryDesc != NULL); - EndPlan(queryDesc->plantree, estate); + EndPlan(queryDesc->plantree, estate); - /* restore saved refcounts. */ - BufferRefCountRestore(estate->es_refcount); + /* restore saved refcounts. */ + BufferRefCountRestore(estate->es_refcount); } /* =============================================================== * =============================================================== - static routines follow + static routines follow * =============================================================== * =============================================================== */ static void ExecCheckPerms(CmdType operation, - int resultRelation, - List *rangeTable, - Query *parseTree) + int resultRelation, + List * rangeTable, + Query * parseTree) { - int i = 1; - Oid relid; - HeapTuple htp; - List *lp; - List *qvars, *tvars; - int32 ok = 1, aclcheck_result = -1; - char *opstr; - NameData rname; - char *userName; - -#define CHECK(MODE) pg_aclcheck(rname.data, userName, MODE) - - userName = GetPgUserName(); - - foreach (lp, rangeTable) { - RangeTblEntry *rte = lfirst(lp); - - relid = rte->relid; - htp = SearchSysCacheTuple(RELOID, - ObjectIdGetDatum(relid), - 0,0,0); - if (!HeapTupleIsValid(htp)) - elog(WARN, "ExecCheckPerms: bogus RT relid: %d", - relid); - strNcpy(rname.data, - ((Form_pg_class) GETSTRUCT(htp))->relname.data, - NAMEDATALEN-1); - if (i == resultRelation) { /* this is the result relation */ - qvars = pull_varnos(parseTree->qual); - tvars = pull_varnos((Node*)parseTree->targetList); - if (intMember(resultRelation, qvars) || - intMember(resultRelation, tvars)) { - /* result relation is scanned */ - ok = ((aclcheck_result = CHECK(ACL_RD)) == ACLCHECK_OK); - opstr = "read"; + int i = 1; + Oid relid; + HeapTuple htp; + List *lp; + List *qvars, + *tvars; + int32 ok = 1, + aclcheck_result = -1; + char *opstr; + NameData rname; + char *userName; + +#define CHECK(MODE) pg_aclcheck(rname.data, userName, MODE) + + userName = GetPgUserName(); + + foreach(lp, rangeTable) + { + RangeTblEntry *rte = lfirst(lp); + + relid = rte->relid; + htp = SearchSysCacheTuple(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (!HeapTupleIsValid(htp)) + elog(WARN, "ExecCheckPerms: bogus RT relid: %d", + relid); + strNcpy(rname.data, + ((Form_pg_class) GETSTRUCT(htp))->relname.data, + NAMEDATALEN - 1); + if (i == resultRelation) + { /* this is the result relation */ + qvars = pull_varnos(parseTree->qual); + tvars = pull_varnos((Node *) parseTree->targetList); + if (intMember(resultRelation, qvars) || + intMember(resultRelation, tvars)) + { + /* result relation is scanned */ + ok = ((aclcheck_result = CHECK(ACL_RD)) == ACLCHECK_OK); + opstr = "read"; + if (!ok) + break; + } + switch (operation) + { + case CMD_INSERT: + ok = ((aclcheck_result = CHECK(ACL_AP)) == ACLCHECK_OK) || + ((aclcheck_result = CHECK(ACL_WR)) == ACLCHECK_OK); + opstr = "append"; + break; + case CMD_NOTIFY: /* what does this mean?? -- jw, 1/6/94 */ + case CMD_DELETE: + case CMD_UPDATE: + ok = ((aclcheck_result = CHECK(ACL_WR)) == ACLCHECK_OK); + opstr = "write"; + break; + default: + elog(WARN, "ExecCheckPerms: bogus operation %d", + operation); + } + } + else + { + /* XXX NOTIFY?? */ + ok = ((aclcheck_result = CHECK(ACL_RD)) == ACLCHECK_OK); + opstr = "read"; + } if (!ok) - break; - } - switch (operation) { - case CMD_INSERT: - ok = ((aclcheck_result = CHECK(ACL_AP)) == ACLCHECK_OK) || - ((aclcheck_result = CHECK(ACL_WR)) == ACLCHECK_OK); - opstr = "append"; - break; - case CMD_NOTIFY: /* what does this mean?? -- jw, 1/6/94 */ - case CMD_DELETE: - case CMD_UPDATE: - ok = ((aclcheck_result = CHECK(ACL_WR)) == ACLCHECK_OK); - opstr = "write"; - break; - default: - elog(WARN, "ExecCheckPerms: bogus operation %d", - operation); - } - } else { - /* XXX NOTIFY?? */ - ok = ((aclcheck_result = CHECK(ACL_RD)) == ACLCHECK_OK); - opstr = "read"; + break; + ++i; } if (!ok) - break; - ++i; - } - if (!ok) { - elog(WARN, "%s: %s", rname.data, aclcheck_error_strings[aclcheck_result]); - } + { + elog(WARN, "%s: %s", rname.data, aclcheck_error_strings[aclcheck_result]); + } } /* ---------------------------------------------------------------- - * InitPlan - * - * Initializes the query plan: open files, allocate storage - * and start up the rule manager + * InitPlan + * + * Initializes the query plan: open files, allocate storage + * and start up the rule manager * ---------------------------------------------------------------- */ -static TupleDesc -InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) -{ - List *rangeTable; - int resultRelation; - Relation intoRelationDesc; - - TupleDesc tupType; - List *targetList; - int len; - - /* ---------------- - * get information from query descriptor - * ---------------- - */ - rangeTable = parseTree->rtable; - resultRelation = parseTree->resultRelation; - - /* ---------------- - * initialize the node's execution state - * ---------------- - */ - estate->es_range_table = rangeTable; - - /* ---------------- - * initialize the BaseId counter so node base_id's - * are assigned correctly. Someday baseid's will have to - * be stored someplace other than estate because they - * should be unique per query planned. - * ---------------- - */ - estate->es_BaseId = 1; - - /* ---------------- - * initialize result relation stuff - * ---------------- - */ - - if (resultRelation != 0 && operation != CMD_SELECT) { - /* ---------------- - * if we have a result relation, open it and +static TupleDesc +InitPlan(CmdType operation, Query * parseTree, Plan * plan, EState * estate) +{ + List *rangeTable; + int resultRelation; + Relation intoRelationDesc; + + TupleDesc tupType; + List *targetList; + int len; - * initialize the result relation info stuff. + /* ---------------- + * get information from query descriptor * ---------------- */ - RelationInfo *resultRelationInfo; - Index resultRelationIndex; - RangeTblEntry *rtentry; - Oid resultRelationOid; - Relation resultRelationDesc; - - resultRelationIndex = resultRelation; - rtentry = rt_fetch(resultRelationIndex, rangeTable); - resultRelationOid = rtentry->relid; - resultRelationDesc = heap_open(resultRelationOid); - - if ( resultRelationDesc->rd_rel->relkind == RELKIND_SEQUENCE ) - elog (WARN, "You can't change sequence relation %s", - resultRelationDesc->rd_rel->relname.data); - - /* Write-lock the result relation right away: if the relation - is used in a subsequent scan, we won't have to elevate the - read-lock set by heap_beginscan to a write-lock (needed by - heap_insert, heap_delete and heap_replace). - This will hopefully prevent some deadlocks. - 01/24/94 */ - RelationSetLockForWrite(resultRelationDesc); - - resultRelationInfo = makeNode(RelationInfo); - resultRelationInfo->ri_RangeTableIndex = resultRelationIndex; - resultRelationInfo->ri_RelationDesc = resultRelationDesc; - resultRelationInfo->ri_NumIndices = 0; - resultRelationInfo->ri_IndexRelationDescs = NULL; - resultRelationInfo->ri_IndexRelationInfo = NULL; + rangeTable = parseTree->rtable; + resultRelation = parseTree->resultRelation; /* ---------------- - * open indices on result relation and save descriptors - * in the result relation information.. + * initialize the node's execution state * ---------------- */ - ExecOpenIndices(resultRelationOid, resultRelationInfo); - - estate->es_result_relation_info = resultRelationInfo; - } else { + estate->es_range_table = rangeTable; + /* ---------------- - * if no result relation, then set state appropriately + * initialize the BaseId counter so node base_id's + * are assigned correctly. Someday baseid's will have to + * be stored someplace other than estate because they + * should be unique per query planned. * ---------------- */ - estate->es_result_relation_info = NULL; - } + estate->es_BaseId = 1; -#ifndef NO_SECURITY - ExecCheckPerms(operation, resultRelation, rangeTable, parseTree); -#endif + /* ---------------- + * initialize result relation stuff + * ---------------- + */ - /* ---------------- - * initialize the executor "tuple" table. - * ---------------- - */ - { - int nSlots = ExecCountSlotsNode(plan); - TupleTable tupleTable = ExecCreateTupleTable(nSlots+10); /* why add ten? - jolly */ - - estate->es_tupleTable = tupleTable; - } - - /* ---------------- - * initialize the private state information for - * all the nodes in the query tree. This opens - * files, allocates storage and leaves us ready - * to start processing tuples.. - * ---------------- - */ - ExecInitNode(plan, estate, NULL); - - /* ---------------- - * get the tuple descriptor describing the type - * of tuples to return.. (this is especially important - * if we are creating a relation with "retrieve into") - * ---------------- - */ - tupType = ExecGetTupType(plan); /* tuple descriptor */ - targetList = plan->targetlist; - len = ExecTargetListLength(targetList); /* number of attributes */ - - /* ---------------- - * now that we have the target list, initialize the junk filter - * if this is a REPLACE or a DELETE query. - * We also init the junk filter if this is an append query - * (there might be some rule lock info there...) - * NOTE: in the future we might want to initialize the junk - * filter for all queries. - * ---------------- - */ - if (operation == CMD_UPDATE || operation == CMD_DELETE || - operation == CMD_INSERT) { - - JunkFilter *j = (JunkFilter*) ExecInitJunkFilter(targetList); - estate->es_junkFilter = j; - } else - estate->es_junkFilter = NULL; - - /* ---------------- - * initialize the "into" relation - * ---------------- - */ - intoRelationDesc = (Relation) NULL; - - if (operation == CMD_SELECT) { - char *intoName; - char archiveMode; - Oid intoRelationId; - TupleDesc tupdesc; - - if (!parseTree->isPortal) { - /* - * a select into table - */ - if (parseTree->into != NULL) { + if (resultRelation != 0 && operation != CMD_SELECT) + { /* ---------------- - * create the "into" relation - * - * note: there is currently no way for the user to - * specify the desired archive mode of the - * "into" relation... + * if we have a result relation, open it and + + * initialize the result relation info stuff. * ---------------- */ - intoName = parseTree->into; - archiveMode = 'n'; - + RelationInfo *resultRelationInfo; + Index resultRelationIndex; + RangeTblEntry *rtentry; + Oid resultRelationOid; + Relation resultRelationDesc; + + resultRelationIndex = resultRelation; + rtentry = rt_fetch(resultRelationIndex, rangeTable); + resultRelationOid = rtentry->relid; + resultRelationDesc = heap_open(resultRelationOid); + + if (resultRelationDesc->rd_rel->relkind == RELKIND_SEQUENCE) + elog(WARN, "You can't change sequence relation %s", + resultRelationDesc->rd_rel->relname.data); + /* - * have to copy tupType to get rid of constraints + * Write-lock the result relation right away: if the relation is + * used in a subsequent scan, we won't have to elevate the + * read-lock set by heap_beginscan to a write-lock (needed by + * heap_insert, heap_delete and heap_replace). This will hopefully + * prevent some deadlocks. - 01/24/94 */ - tupdesc = CreateTupleDescCopy (tupType); - - /* fixup to prevent zero-length columns in create */ - setVarAttrLenForCreateTable(tupdesc, targetList, rangeTable); - - intoRelationId = heap_create(intoName, - intoName, /* not used */ - archiveMode, - DEFAULT_SMGR, - tupdesc); -#ifdef NOT_USED /* it's copy ... */ - resetVarAttrLenForCreateTable(tupdesc); -#endif - FreeTupleDesc (tupdesc); + RelationSetLockForWrite(resultRelationDesc); + + resultRelationInfo = makeNode(RelationInfo); + resultRelationInfo->ri_RangeTableIndex = resultRelationIndex; + resultRelationInfo->ri_RelationDesc = resultRelationDesc; + resultRelationInfo->ri_NumIndices = 0; + resultRelationInfo->ri_IndexRelationDescs = NULL; + resultRelationInfo->ri_IndexRelationInfo = NULL; /* ---------------- - * XXX rather than having to call setheapoverride(true) - * and then back to false, we should change the - * arguments to heap_open() instead.. + * open indices on result relation and save descriptors + * in the result relation information.. * ---------------- */ - setheapoverride(true); - - intoRelationDesc = heap_open(intoRelationId); - - setheapoverride(false); - } + ExecOpenIndices(resultRelationOid, resultRelationInfo); + + estate->es_result_relation_info = resultRelationInfo; } - } + else + { + /* ---------------- + * if no result relation, then set state appropriately + * ---------------- + */ + estate->es_result_relation_info = NULL; + } + +#ifndef NO_SECURITY + ExecCheckPerms(operation, resultRelation, rangeTable, parseTree); +#endif + + /* ---------------- + * initialize the executor "tuple" table. + * ---------------- + */ + { + int nSlots = ExecCountSlotsNode(plan); + TupleTable tupleTable = ExecCreateTupleTable(nSlots + 10); /* why add ten? - jolly */ - estate->es_into_relation_descriptor = intoRelationDesc; + estate->es_tupleTable = tupleTable; + } - /* ---------------- - * return the type information.. - * ---------------- - */ + /* ---------------- + * initialize the private state information for + * all the nodes in the query tree. This opens + * files, allocates storage and leaves us ready + * to start processing tuples.. + * ---------------- + */ + ExecInitNode(plan, estate, NULL); + + /* ---------------- + * get the tuple descriptor describing the type + * of tuples to return.. (this is especially important + * if we are creating a relation with "retrieve into") + * ---------------- + */ + tupType = ExecGetTupType(plan); /* tuple descriptor */ + targetList = plan->targetlist; + len = ExecTargetListLength(targetList); /* number of attributes */ + + /* ---------------- + * now that we have the target list, initialize the junk filter + * if this is a REPLACE or a DELETE query. + * We also init the junk filter if this is an append query + * (there might be some rule lock info there...) + * NOTE: in the future we might want to initialize the junk + * filter for all queries. + * ---------------- + */ + if (operation == CMD_UPDATE || operation == CMD_DELETE || + operation == CMD_INSERT) + { + + JunkFilter *j = (JunkFilter *) ExecInitJunkFilter(targetList); + + estate->es_junkFilter = j; + } + else + estate->es_junkFilter = NULL; + + /* ---------------- + * initialize the "into" relation + * ---------------- + */ + intoRelationDesc = (Relation) NULL; + + if (operation == CMD_SELECT) + { + char *intoName; + char archiveMode; + Oid intoRelationId; + TupleDesc tupdesc; + + if (!parseTree->isPortal) + { + + /* + * a select into table + */ + if (parseTree->into != NULL) + { + /* ---------------- + * create the "into" relation + * + * note: there is currently no way for the user to + * specify the desired archive mode of the + * "into" relation... + * ---------------- + */ + intoName = parseTree->into; + archiveMode = 'n'; + + /* + * have to copy tupType to get rid of constraints + */ + tupdesc = CreateTupleDescCopy(tupType); + + /* fixup to prevent zero-length columns in create */ + setVarAttrLenForCreateTable(tupdesc, targetList, rangeTable); + + intoRelationId = heap_create(intoName, + intoName, /* not used */ + archiveMode, + DEFAULT_SMGR, + tupdesc); +#ifdef NOT_USED /* it's copy ... */ + resetVarAttrLenForCreateTable(tupdesc); +#endif + FreeTupleDesc(tupdesc); + + /* ---------------- + * XXX rather than having to call setheapoverride(true) + * and then back to false, we should change the + * arguments to heap_open() instead.. + * ---------------- + */ + setheapoverride(true); + + intoRelationDesc = heap_open(intoRelationId); + + setheapoverride(false); + } + } + } + + estate->es_into_relation_descriptor = intoRelationDesc; + + /* ---------------- + * return the type information.. + * ---------------- + */ /* - attinfo = (AttrInfo *)palloc(sizeof(AttrInfo)); - attinfo->numAttr = len; - attinfo->attrs = tupType->attrs; + attinfo = (AttrInfo *)palloc(sizeof(AttrInfo)); + attinfo->numAttr = len; + attinfo->attrs = tupType->attrs; */ - return tupType; + return tupType; } /* ---------------------------------------------------------------- - * EndPlan - * - * Cleans up the query plan -- closes files and free up storages + * EndPlan + * + * Cleans up the query plan -- closes files and free up storages * ---------------------------------------------------------------- */ static void -EndPlan(Plan *plan, EState *estate) +EndPlan(Plan * plan, EState * estate) { - RelationInfo *resultRelationInfo; - Relation intoRelationDesc; - - /* ---------------- - * get information from state - * ---------------- - */ - resultRelationInfo = estate->es_result_relation_info; - intoRelationDesc = estate->es_into_relation_descriptor; - - /* ---------------- - * shut down the query - * ---------------- - */ - ExecEndNode(plan, plan); - - /* ---------------- - * destroy the executor "tuple" table. - * ---------------- - */ - { - TupleTable tupleTable = (TupleTable) estate->es_tupleTable; - ExecDestroyTupleTable(tupleTable,true); /* was missing last arg */ - estate->es_tupleTable = NULL; - } - - /* ---------------- - * close the result relations if necessary - * ---------------- - */ - if (resultRelationInfo != NULL) { - Relation resultRelationDesc; - - resultRelationDesc = resultRelationInfo->ri_RelationDesc; - heap_close(resultRelationDesc); - + RelationInfo *resultRelationInfo; + Relation intoRelationDesc; + /* ---------------- - * close indices on the result relation + * get information from state * ---------------- */ - ExecCloseIndices(resultRelationInfo); - } - - /* ---------------- - * close the "into" relation if necessary - * ---------------- - */ - if (intoRelationDesc != NULL) { - heap_close(intoRelationDesc); - } + resultRelationInfo = estate->es_result_relation_info; + intoRelationDesc = estate->es_into_relation_descriptor; + + /* ---------------- + * shut down the query + * ---------------- + */ + ExecEndNode(plan, plan); + + /* ---------------- + * destroy the executor "tuple" table. + * ---------------- + */ + { + TupleTable tupleTable = (TupleTable) estate->es_tupleTable; + + ExecDestroyTupleTable(tupleTable, true); /* was missing last arg */ + estate->es_tupleTable = NULL; + } + + /* ---------------- + * close the result relations if necessary + * ---------------- + */ + if (resultRelationInfo != NULL) + { + Relation resultRelationDesc; + + resultRelationDesc = resultRelationInfo->ri_RelationDesc; + heap_close(resultRelationDesc); + + /* ---------------- + * close indices on the result relation + * ---------------- + */ + ExecCloseIndices(resultRelationInfo); + } + + /* ---------------- + * close the "into" relation if necessary + * ---------------- + */ + if (intoRelationDesc != NULL) + { + heap_close(intoRelationDesc); + } } /* ---------------------------------------------------------------- - * ExecutePlan - * - * processes the query plan to retrieve 'tupleCount' tuples in the - * direction specified. - * Retrieves all tuples if tupleCount is 0 + * ExecutePlan + * + * processes the query plan to retrieve 'tupleCount' tuples in the + * direction specified. + * Retrieves all tuples if tupleCount is 0 + * + * result is either a slot containing a tuple in the case + * of a RETRIEVE or NULL otherwise. * - * result is either a slot containing a tuple in the case - * of a RETRIEVE or NULL otherwise. - * * ---------------------------------------------------------------- */ @@ -660,688 +698,704 @@ EndPlan(Plan *plan, EState *estate) user can see it*/ static TupleTableSlot * -ExecutePlan(EState *estate, - Plan *plan, - Query *parseTree, - CmdType operation, - int numberTuples, - ScanDirection direction, - void (*printfunc)()) +ExecutePlan(EState * estate, + Plan * plan, + Query * parseTree, + CmdType operation, + int numberTuples, + ScanDirection direction, + void (*printfunc) ()) { - JunkFilter *junkfilter; - - TupleTableSlot *slot; - ItemPointer tupleid = NULL; - ItemPointerData tuple_ctid; - int current_tuple_count; - TupleTableSlot *result; - - /* ---------------- - * initialize local variables - * ---------------- - */ - slot = NULL; - current_tuple_count = 0; - result = NULL; - - /* ---------------- - * Set the direction. - * ---------------- - */ - estate->es_direction = direction; - - /* ---------------- - * Loop until we've processed the proper number - * of tuples from the plan.. - * ---------------- - */ - - for(;;) { - if (operation != CMD_NOTIFY) { - /* ---------------- - * Execute the plan and obtain a tuple - * ---------------- - */ - /* at the top level, the parent of a plan (2nd arg) is itself */ - slot = ExecProcNode(plan,plan); - - /* ---------------- - * if the tuple is null, then we assume - * there is nothing more to process so - * we just return null... - * ---------------- - */ - if (TupIsNull(slot)) { - result = NULL; - break; - } - } - + JunkFilter *junkfilter; + + TupleTableSlot *slot; + ItemPointer tupleid = NULL; + ItemPointerData tuple_ctid; + int current_tuple_count; + TupleTableSlot *result; + /* ---------------- - * if we have a junk filter, then project a new - * tuple with the junk removed. - * - * Store this new "clean" tuple in the place of the - * original tuple. - * - * Also, extract all the junk ifnormation we need. + * initialize local variables * ---------------- */ - if ((junkfilter = estate->es_junkFilter) != (JunkFilter*)NULL) { - Datum datum; -/* NameData attrName; */ - HeapTuple newTuple; - bool isNull; - - /* --------------- - * extract the 'ctid' junk attribute. - * --------------- - */ - if (operation == CMD_UPDATE || operation == CMD_DELETE) { - if (! ExecGetJunkAttribute(junkfilter, - slot, - "ctid", - &datum, - &isNull)) - elog(WARN,"ExecutePlan: NO (junk) `ctid' was found!"); - - if (isNull) - elog(WARN,"ExecutePlan: (junk) `ctid' is NULL!"); - - tupleid = (ItemPointer) DatumGetPointer(datum); - tuple_ctid = *tupleid; /* make sure we don't free the ctid!! */ - tupleid = &tuple_ctid; - } - - /* --------------- - * Finally create a new "clean" tuple with all junk attributes - * removed - * --------------- - */ - newTuple = ExecRemoveJunk(junkfilter, slot); - - slot = ExecStoreTuple(newTuple, /* tuple to store */ - slot, /* destination slot */ - InvalidBuffer,/* this tuple has no buffer */ - true); /* tuple should be pfreed */ - } /* if (junkfilter... */ - + slot = NULL; + current_tuple_count = 0; + result = NULL; + /* ---------------- - * now that we have a tuple, do the appropriate thing - * with it.. either return it to the user, add - * it to a relation someplace, delete it from a - * relation, or modify some of it's attributes. + * Set the direction. * ---------------- */ - - switch(operation) { - case CMD_SELECT: - ExecRetrieve(slot, /* slot containing tuple */ - printfunc, /* print function */ - estate); /* */ - result = slot; - break; - - case CMD_INSERT: - ExecAppend(slot, tupleid, estate); - result = NULL; - break; - - case CMD_DELETE: - ExecDelete(slot, tupleid, estate); - result = NULL; - break; - - case CMD_UPDATE: - ExecReplace(slot, tupleid, estate, parseTree); - result = NULL; - break; - - /* Total hack. I'm ignoring any accessor functions for - Relation, RelationTupleForm, NameData. - Assuming that NameData.data has offset 0. - */ - case CMD_NOTIFY: { - RelationInfo *rInfo = estate->es_result_relation_info; - Relation rDesc = rInfo->ri_RelationDesc; - Async_Notify(rDesc->rd_rel->relname.data); - result = NULL; - current_tuple_count = 0; - numberTuples = 1; - elog(DEBUG, "ExecNotify %s",&rDesc->rd_rel->relname); - } - break; - - default: - elog(DEBUG, "ExecutePlan: unknown operation in queryDesc"); - result = NULL; - break; + estate->es_direction = direction; + + /* ---------------- + * Loop until we've processed the proper number + * of tuples from the plan.. + * ---------------- + */ + + for (;;) + { + if (operation != CMD_NOTIFY) + { + /* ---------------- + * Execute the plan and obtain a tuple + * ---------------- + */ + /* at the top level, the parent of a plan (2nd arg) is itself */ + slot = ExecProcNode(plan, plan); + + /* ---------------- + * if the tuple is null, then we assume + * there is nothing more to process so + * we just return null... + * ---------------- + */ + if (TupIsNull(slot)) + { + result = NULL; + break; + } + } + + /* ---------------- + * if we have a junk filter, then project a new + * tuple with the junk removed. + * + * Store this new "clean" tuple in the place of the + * original tuple. + * + * Also, extract all the junk ifnormation we need. + * ---------------- + */ + if ((junkfilter = estate->es_junkFilter) != (JunkFilter *) NULL) + { + Datum datum; + +/* NameData attrName; */ + HeapTuple newTuple; + bool isNull; + + /* --------------- + * extract the 'ctid' junk attribute. + * --------------- + */ + if (operation == CMD_UPDATE || operation == CMD_DELETE) + { + if (!ExecGetJunkAttribute(junkfilter, + slot, + "ctid", + &datum, + &isNull)) + elog(WARN, "ExecutePlan: NO (junk) `ctid' was found!"); + + if (isNull) + elog(WARN, "ExecutePlan: (junk) `ctid' is NULL!"); + + tupleid = (ItemPointer) DatumGetPointer(datum); + tuple_ctid = *tupleid; /* make sure we don't free the + * ctid!! */ + tupleid = &tuple_ctid; + } + + /* --------------- + * Finally create a new "clean" tuple with all junk attributes + * removed + * --------------- + */ + newTuple = ExecRemoveJunk(junkfilter, slot); + + slot = ExecStoreTuple(newTuple, /* tuple to store */ + slot, /* destination slot */ + InvalidBuffer, /* this tuple has no + * buffer */ + true); /* tuple should be pfreed */ + } /* if (junkfilter... */ + + /* ---------------- + * now that we have a tuple, do the appropriate thing + * with it.. either return it to the user, add + * it to a relation someplace, delete it from a + * relation, or modify some of it's attributes. + * ---------------- + */ + + switch (operation) + { + case CMD_SELECT: + ExecRetrieve(slot, /* slot containing tuple */ + printfunc, /* print function */ + estate); /* */ + result = slot; + break; + + case CMD_INSERT: + ExecAppend(slot, tupleid, estate); + result = NULL; + break; + + case CMD_DELETE: + ExecDelete(slot, tupleid, estate); + result = NULL; + break; + + case CMD_UPDATE: + ExecReplace(slot, tupleid, estate, parseTree); + result = NULL; + break; + + /* + * Total hack. I'm ignoring any accessor functions for + * Relation, RelationTupleForm, NameData. Assuming that + * NameData.data has offset 0. + */ + case CMD_NOTIFY: + { + RelationInfo *rInfo = estate->es_result_relation_info; + Relation rDesc = rInfo->ri_RelationDesc; + + Async_Notify(rDesc->rd_rel->relname.data); + result = NULL; + current_tuple_count = 0; + numberTuples = 1; + elog(DEBUG, "ExecNotify %s", &rDesc->rd_rel->relname); + } + break; + + default: + elog(DEBUG, "ExecutePlan: unknown operation in queryDesc"); + result = NULL; + break; + } + /* ---------------- + * check our tuple count.. if we've returned the + * proper number then return, else loop again and + * process more tuples.. + * ---------------- + */ + current_tuple_count += 1; + if (numberTuples == current_tuple_count) + break; } + /* ---------------- - * check our tuple count.. if we've returned the - * proper number then return, else loop again and - * process more tuples.. + * here, result is either a slot containing a tuple in the case + * of a RETRIEVE or NULL otherwise. * ---------------- */ - current_tuple_count += 1; - if (numberTuples == current_tuple_count) - break; - } - - /* ---------------- - * here, result is either a slot containing a tuple in the case - * of a RETRIEVE or NULL otherwise. - * ---------------- - */ - return result; + return result; } /* ---------------------------------------------------------------- - * ExecRetrieve + * ExecRetrieve * - * RETRIEVEs are easy.. we just pass the tuple to the appropriate - * print function. The only complexity is when we do a - * "retrieve into", in which case we insert the tuple into - * the appropriate relation (note: this is a newly created relation - * so we don't need to worry about indices or locks.) + * RETRIEVEs are easy.. we just pass the tuple to the appropriate + * print function. The only complexity is when we do a + * "retrieve into", in which case we insert the tuple into + * the appropriate relation (note: this is a newly created relation + * so we don't need to worry about indices or locks.) * ---------------------------------------------------------------- */ static void -ExecRetrieve(TupleTableSlot *slot, - void (*printfunc)(), - EState *estate) +ExecRetrieve(TupleTableSlot * slot, + void (*printfunc) (), + EState * estate) { - HeapTuple tuple; - TupleDesc attrtype; - - /* ---------------- - * get the heap tuple out of the tuple table slot - * ---------------- - */ - tuple = slot->val; - attrtype = slot->ttc_tupleDescriptor; - - /* ---------------- - * insert the tuple into the "into relation" - * ---------------- - */ - if ( estate->es_into_relation_descriptor != NULL ) - { - heap_insert (estate->es_into_relation_descriptor, tuple); - IncrAppended(); - } - - /* ---------------- - * send the tuple to the front end (or the screen) - * ---------------- - */ - (*printfunc)(tuple, attrtype); - IncrRetrieved(); - (estate->es_processed)++; + HeapTuple tuple; + TupleDesc attrtype; + + /* ---------------- + * get the heap tuple out of the tuple table slot + * ---------------- + */ + tuple = slot->val; + attrtype = slot->ttc_tupleDescriptor; + + /* ---------------- + * insert the tuple into the "into relation" + * ---------------- + */ + if (estate->es_into_relation_descriptor != NULL) + { + heap_insert(estate->es_into_relation_descriptor, tuple); + IncrAppended(); + } + + /* ---------------- + * send the tuple to the front end (or the screen) + * ---------------- + */ + (*printfunc) (tuple, attrtype); + IncrRetrieved(); + (estate->es_processed)++; } /* ---------------------------------------------------------------- - * ExecAppend + * ExecAppend * - * APPENDs are trickier.. we have to insert the tuple into - * the base relation and insert appropriate tuples into the - * index relations. + * APPENDs are trickier.. we have to insert the tuple into + * the base relation and insert appropriate tuples into the + * index relations. * ---------------------------------------------------------------- */ static void -ExecAppend(TupleTableSlot *slot, - ItemPointer tupleid, - EState *estate) +ExecAppend(TupleTableSlot * slot, + ItemPointer tupleid, + EState * estate) { - HeapTuple tuple; - RelationInfo *resultRelationInfo; - Relation resultRelationDesc; - int numIndices; - Oid newId; - - /* ---------------- - * get the heap tuple out of the tuple table slot - * ---------------- - */ - tuple = slot->val; - - /* ---------------- - * get information on the result relation - * ---------------- - */ - resultRelationInfo = estate->es_result_relation_info; - resultRelationDesc = resultRelationInfo->ri_RelationDesc; - - /* ---------------- - * have to add code to preform unique checking here. - * cim -12/1/89 - * ---------------- - */ - - /* BEFORE ROW INSERT Triggers */ - if ( resultRelationDesc->trigdesc && - resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0 ) - { - HeapTuple newtuple; - - newtuple = ExecBRInsertTriggers (resultRelationDesc, tuple); - - if ( newtuple == NULL ) /* "do nothing" */ - return; - - if ( newtuple != tuple ) /* modified by Trigger(s) */ - { - Assert ( slot->ttc_shouldFree ); - pfree (tuple); - slot->val = tuple = newtuple; - } - } - - /* ---------------- - * Check the constraints of a tuple - * ---------------- - */ - - if ( resultRelationDesc->rd_att->constr ) - { - HeapTuple newtuple; - - newtuple = ExecConstraints ("ExecAppend", resultRelationDesc, tuple); - - if ( newtuple != tuple ) /* modified by DEFAULT */ - { - Assert ( slot->ttc_shouldFree ); - pfree (tuple); - slot->val = tuple = newtuple; - } - } - - /* ---------------- - * insert the tuple - * ---------------- - */ - newId = heap_insert(resultRelationDesc, /* relation desc */ - tuple); /* heap tuple */ - IncrAppended(); - - /* ---------------- - * process indices - * - * Note: heap_insert adds a new tuple to a relation. As a side - * effect, the tupleid of the new tuple is placed in the new - * tuple's t_ctid field. - * ---------------- - */ - numIndices = resultRelationInfo->ri_NumIndices; - if (numIndices > 0) { - ExecInsertIndexTuples(slot, &(tuple->t_ctid), estate, false); - } - (estate->es_processed)++; - estate->es_lastoid = newId; - - /* AFTER ROW INSERT Triggers */ - if ( resultRelationDesc->trigdesc && - resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0 ) - ExecARInsertTriggers (resultRelationDesc, tuple); + HeapTuple tuple; + RelationInfo *resultRelationInfo; + Relation resultRelationDesc; + int numIndices; + Oid newId; + + /* ---------------- + * get the heap tuple out of the tuple table slot + * ---------------- + */ + tuple = slot->val; + + /* ---------------- + * get information on the result relation + * ---------------- + */ + resultRelationInfo = estate->es_result_relation_info; + resultRelationDesc = resultRelationInfo->ri_RelationDesc; + + /* ---------------- + * have to add code to preform unique checking here. + * cim -12/1/89 + * ---------------- + */ + + /* BEFORE ROW INSERT Triggers */ + if (resultRelationDesc->trigdesc && + resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) + { + HeapTuple newtuple; + + newtuple = ExecBRInsertTriggers(resultRelationDesc, tuple); + + if (newtuple == NULL) /* "do nothing" */ + return; + + if (newtuple != tuple) /* modified by Trigger(s) */ + { + Assert(slot->ttc_shouldFree); + pfree(tuple); + slot->val = tuple = newtuple; + } + } + + /* ---------------- + * Check the constraints of a tuple + * ---------------- + */ + + if (resultRelationDesc->rd_att->constr) + { + HeapTuple newtuple; + + newtuple = ExecConstraints("ExecAppend", resultRelationDesc, tuple); + + if (newtuple != tuple) /* modified by DEFAULT */ + { + Assert(slot->ttc_shouldFree); + pfree(tuple); + slot->val = tuple = newtuple; + } + } + + /* ---------------- + * insert the tuple + * ---------------- + */ + newId = heap_insert(resultRelationDesc, /* relation desc */ + tuple); /* heap tuple */ + IncrAppended(); + + /* ---------------- + * process indices + * + * Note: heap_insert adds a new tuple to a relation. As a side + * effect, the tupleid of the new tuple is placed in the new + * tuple's t_ctid field. + * ---------------- + */ + numIndices = resultRelationInfo->ri_NumIndices; + if (numIndices > 0) + { + ExecInsertIndexTuples(slot, &(tuple->t_ctid), estate, false); + } + (estate->es_processed)++; + estate->es_lastoid = newId; + + /* AFTER ROW INSERT Triggers */ + if (resultRelationDesc->trigdesc && + resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0) + ExecARInsertTriggers(resultRelationDesc, tuple); } /* ---------------------------------------------------------------- - * ExecDelete + * ExecDelete * - * DELETE is like append, we delete the tuple and its - * index tuples. + * DELETE is like append, we delete the tuple and its + * index tuples. * ---------------------------------------------------------------- */ static void -ExecDelete(TupleTableSlot *slot, - ItemPointer tupleid, - EState *estate) +ExecDelete(TupleTableSlot * slot, + ItemPointer tupleid, + EState * estate) { - RelationInfo *resultRelationInfo; - Relation resultRelationDesc; - - /* ---------------- - * get the result relation information - * ---------------- - */ - resultRelationInfo = estate->es_result_relation_info; - resultRelationDesc = resultRelationInfo->ri_RelationDesc; - - /* BEFORE ROW DELETE Triggers */ - if ( resultRelationDesc->trigdesc && - resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_DELETE] > 0 ) - { - bool dodelete; - - dodelete = ExecBRDeleteTriggers (resultRelationDesc, tupleid); - - if ( !dodelete ) /* "do nothing" */ - return; - } - - /* ---------------- - * delete the tuple - * ---------------- - */ - if ( heap_delete(resultRelationDesc, /* relation desc */ - tupleid) ) /* item pointer to tuple */ - return; - - IncrDeleted(); - (estate->es_processed)++; - - /* ---------------- - * Note: Normally one would think that we have to - * delete index tuples associated with the - * heap tuple now.. - * - * ... but in POSTGRES, we have no need to do this - * because the vacuum daemon automatically - * opens an index scan and deletes index tuples - * when it finds deleted heap tuples. -cim 9/27/89 - * ---------------- - */ - - /* AFTER ROW DELETE Triggers */ - if ( resultRelationDesc->trigdesc && - resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0 ) - ExecARDeleteTriggers (resultRelationDesc, tupleid); + RelationInfo *resultRelationInfo; + Relation resultRelationDesc; + + /* ---------------- + * get the result relation information + * ---------------- + */ + resultRelationInfo = estate->es_result_relation_info; + resultRelationDesc = resultRelationInfo->ri_RelationDesc; + + /* BEFORE ROW DELETE Triggers */ + if (resultRelationDesc->trigdesc && + resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_DELETE] > 0) + { + bool dodelete; + + dodelete = ExecBRDeleteTriggers(resultRelationDesc, tupleid); + + if (!dodelete) /* "do nothing" */ + return; + } + + /* ---------------- + * delete the tuple + * ---------------- + */ + if (heap_delete(resultRelationDesc, /* relation desc */ + tupleid)) /* item pointer to tuple */ + return; + + IncrDeleted(); + (estate->es_processed)++; + + /* ---------------- + * Note: Normally one would think that we have to + * delete index tuples associated with the + * heap tuple now.. + * + * ... but in POSTGRES, we have no need to do this + * because the vacuum daemon automatically + * opens an index scan and deletes index tuples + * when it finds deleted heap tuples. -cim 9/27/89 + * ---------------- + */ + + /* AFTER ROW DELETE Triggers */ + if (resultRelationDesc->trigdesc && + resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0) + ExecARDeleteTriggers(resultRelationDesc, tupleid); } /* ---------------------------------------------------------------- - * ExecReplace + * ExecReplace * - * note: we can't run replace queries with transactions - * off because replaces are actually appends and our - * scan will mistakenly loop forever, replacing the tuple - * it just appended.. This should be fixed but until it - * is, we don't want to get stuck in an infinite loop - * which corrupts your database.. + * note: we can't run replace queries with transactions + * off because replaces are actually appends and our + * scan will mistakenly loop forever, replacing the tuple + * it just appended.. This should be fixed but until it + * is, we don't want to get stuck in an infinite loop + * which corrupts your database.. * ---------------------------------------------------------------- */ static void -ExecReplace(TupleTableSlot *slot, - ItemPointer tupleid, - EState *estate, - Query *parseTree) +ExecReplace(TupleTableSlot * slot, + ItemPointer tupleid, + EState * estate, + Query * parseTree) { - HeapTuple tuple; - RelationInfo *resultRelationInfo; - Relation resultRelationDesc; - int numIndices; - - /* ---------------- - * abort the operation if not running transactions - * ---------------- - */ - if (IsBootstrapProcessingMode()) { - elog(DEBUG, "ExecReplace: replace can't run without transactions"); - return; - } - - /* ---------------- - * get the heap tuple out of the tuple table slot - * ---------------- - */ - tuple = slot->val; - - /* ---------------- - * get the result relation information - * ---------------- - */ - resultRelationInfo = estate->es_result_relation_info; - resultRelationDesc = resultRelationInfo->ri_RelationDesc; - - /* ---------------- - * have to add code to preform unique checking here. - * in the event of unique tuples, this becomes a deletion - * of the original tuple affected by the replace. - * cim -12/1/89 - * ---------------- - */ - - /* BEFORE ROW UPDATE Triggers */ - if ( resultRelationDesc->trigdesc && - resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0 ) - { - HeapTuple newtuple; - - newtuple = ExecBRUpdateTriggers (resultRelationDesc, tupleid, tuple); - - if ( newtuple == NULL ) /* "do nothing" */ - return; - - if ( newtuple != tuple ) /* modified by Trigger(s) */ - { - Assert ( slot->ttc_shouldFree ); - pfree (tuple); - slot->val = tuple = newtuple; - } - } - - /* ---------------- - * Check the constraints of a tuple - * ---------------- - */ - - if ( resultRelationDesc->rd_att->constr ) - { - HeapTuple newtuple; - - newtuple = ExecConstraints ("ExecReplace", resultRelationDesc, tuple); - - if ( newtuple != tuple ) /* modified by DEFAULT */ - { - Assert ( slot->ttc_shouldFree ); - pfree (tuple); - slot->val = tuple = newtuple; - } - } - - /* ---------------- - * replace the heap tuple - * - * Don't want to continue if our heap_replace didn't actually - * do a replace. This would be the case if heap_replace - * detected a non-functional update. -kw 12/30/93 - * ---------------- - */ - if (heap_replace(resultRelationDesc, /* relation desc */ - tupleid, /* item ptr of tuple to replace */ - tuple)) { /* replacement heap tuple */ - return; - } - - IncrReplaced(); - (estate->es_processed)++; - - /* ---------------- - * Note: instead of having to update the old index tuples - * associated with the heap tuple, all we do is form - * and insert new index tuples.. This is because - * replaces are actually deletes and inserts and - * index tuple deletion is done automagically by - * the vaccuum deamon.. All we do is insert new - * index tuples. -cim 9/27/89 - * ---------------- - */ - - /* ---------------- - * process indices - * - * heap_replace updates a tuple in the base relation by invalidating - * it and then appending a new tuple to the relation. As a side - * effect, the tupleid of the new tuple is placed in the new - * tuple's t_ctid field. So we now insert index tuples using - * the new tupleid stored there. - * ---------------- - */ - - numIndices = resultRelationInfo->ri_NumIndices; - if (numIndices > 0) { - ExecInsertIndexTuples(slot, &(tuple->t_ctid), estate, true); - } - - /* AFTER ROW UPDATE Triggers */ - if ( resultRelationDesc->trigdesc && - resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0 ) - ExecARUpdateTriggers (resultRelationDesc, tupleid, tuple); + HeapTuple tuple; + RelationInfo *resultRelationInfo; + Relation resultRelationDesc; + int numIndices; + + /* ---------------- + * abort the operation if not running transactions + * ---------------- + */ + if (IsBootstrapProcessingMode()) + { + elog(DEBUG, "ExecReplace: replace can't run without transactions"); + return; + } + + /* ---------------- + * get the heap tuple out of the tuple table slot + * ---------------- + */ + tuple = slot->val; + + /* ---------------- + * get the result relation information + * ---------------- + */ + resultRelationInfo = estate->es_result_relation_info; + resultRelationDesc = resultRelationInfo->ri_RelationDesc; + + /* ---------------- + * have to add code to preform unique checking here. + * in the event of unique tuples, this becomes a deletion + * of the original tuple affected by the replace. + * cim -12/1/89 + * ---------------- + */ + + /* BEFORE ROW UPDATE Triggers */ + if (resultRelationDesc->trigdesc && + resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) + { + HeapTuple newtuple; + + newtuple = ExecBRUpdateTriggers(resultRelationDesc, tupleid, tuple); + + if (newtuple == NULL) /* "do nothing" */ + return; + + if (newtuple != tuple) /* modified by Trigger(s) */ + { + Assert(slot->ttc_shouldFree); + pfree(tuple); + slot->val = tuple = newtuple; + } + } + + /* ---------------- + * Check the constraints of a tuple + * ---------------- + */ + + if (resultRelationDesc->rd_att->constr) + { + HeapTuple newtuple; + + newtuple = ExecConstraints("ExecReplace", resultRelationDesc, tuple); + + if (newtuple != tuple) /* modified by DEFAULT */ + { + Assert(slot->ttc_shouldFree); + pfree(tuple); + slot->val = tuple = newtuple; + } + } + + /* ---------------- + * replace the heap tuple + * + * Don't want to continue if our heap_replace didn't actually + * do a replace. This would be the case if heap_replace + * detected a non-functional update. -kw 12/30/93 + * ---------------- + */ + if (heap_replace(resultRelationDesc, /* relation desc */ + tupleid, /* item ptr of tuple to replace */ + tuple)) + { /* replacement heap tuple */ + return; + } + + IncrReplaced(); + (estate->es_processed)++; + + /* ---------------- + * Note: instead of having to update the old index tuples + * associated with the heap tuple, all we do is form + * and insert new index tuples.. This is because + * replaces are actually deletes and inserts and + * index tuple deletion is done automagically by + * the vaccuum deamon.. All we do is insert new + * index tuples. -cim 9/27/89 + * ---------------- + */ + + /* ---------------- + * process indices + * + * heap_replace updates a tuple in the base relation by invalidating + * it and then appending a new tuple to the relation. As a side + * effect, the tupleid of the new tuple is placed in the new + * tuple's t_ctid field. So we now insert index tuples using + * the new tupleid stored there. + * ---------------- + */ + + numIndices = resultRelationInfo->ri_NumIndices; + if (numIndices > 0) + { + ExecInsertIndexTuples(slot, &(tuple->t_ctid), estate, true); + } + + /* AFTER ROW UPDATE Triggers */ + if (resultRelationDesc->trigdesc && + resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0) + ExecARUpdateTriggers(resultRelationDesc, tupleid, tuple); } -static HeapTuple -ExecAttrDefault (Relation rel, HeapTuple tuple) +static HeapTuple +ExecAttrDefault(Relation rel, HeapTuple tuple) { - int ndef = rel->rd_att->constr->num_defval; - AttrDefault *attrdef = rel->rd_att->constr->defval; - ExprContext *econtext = makeNode (ExprContext); - HeapTuple newtuple; - Node *expr; - bool isnull; - bool isdone; - Datum val; - Datum *replValue = NULL; - char *replNull = NULL; - char *repl = NULL; - int i; - - econtext->ecxt_scantuple = NULL; /* scan tuple slot */ - econtext->ecxt_innertuple = NULL; /* inner tuple slot */ - econtext->ecxt_outertuple = NULL; /* outer tuple slot */ - econtext->ecxt_relation = NULL; /* relation */ - econtext->ecxt_relid = 0; /* relid */ - econtext->ecxt_param_list_info = NULL; /* param list info */ - econtext->ecxt_range_table = NULL; /* range table */ - for (i = 0; i < ndef; i++) - { - if ( !heap_attisnull (tuple, attrdef[i].adnum) ) - continue; - expr = (Node*) stringToNode (attrdef[i].adbin); - - val = ExecEvalExpr (expr, econtext, &isnull, &isdone); - - pfree (expr); - - if ( isnull ) - continue; - - if ( repl == NULL ) - { - repl = (char*) palloc (rel->rd_att->natts * sizeof (char)); - replNull = (char*) palloc (rel->rd_att->natts * sizeof (char)); - replValue = (Datum*) palloc (rel->rd_att->natts * sizeof (Datum)); - memset (repl, ' ', rel->rd_att->natts * sizeof (char)); - } - - repl[attrdef[i].adnum - 1] = 'r'; - replNull[attrdef[i].adnum - 1] = ' '; - replValue[attrdef[i].adnum - 1] = val; - - } - - pfree (econtext); - - if ( repl == NULL ) - return (tuple); - - newtuple = heap_modifytuple (tuple, InvalidBuffer, rel, replValue, replNull, repl); - - pfree (repl); - pfree (replNull); - pfree (replValue); - - return (newtuple); - + int ndef = rel->rd_att->constr->num_defval; + AttrDefault *attrdef = rel->rd_att->constr->defval; + ExprContext *econtext = makeNode(ExprContext); + HeapTuple newtuple; + Node *expr; + bool isnull; + bool isdone; + Datum val; + Datum *replValue = NULL; + char *replNull = NULL; + char *repl = NULL; + int i; + + econtext->ecxt_scantuple = NULL; /* scan tuple slot */ + econtext->ecxt_innertuple = NULL; /* inner tuple slot */ + econtext->ecxt_outertuple = NULL; /* outer tuple slot */ + econtext->ecxt_relation = NULL; /* relation */ + econtext->ecxt_relid = 0; /* relid */ + econtext->ecxt_param_list_info = NULL; /* param list info */ + econtext->ecxt_range_table = NULL; /* range table */ + for (i = 0; i < ndef; i++) + { + if (!heap_attisnull(tuple, attrdef[i].adnum)) + continue; + expr = (Node *) stringToNode(attrdef[i].adbin); + + val = ExecEvalExpr(expr, econtext, &isnull, &isdone); + + pfree(expr); + + if (isnull) + continue; + + if (repl == NULL) + { + repl = (char *) palloc(rel->rd_att->natts * sizeof(char)); + replNull = (char *) palloc(rel->rd_att->natts * sizeof(char)); + replValue = (Datum *) palloc(rel->rd_att->natts * sizeof(Datum)); + memset(repl, ' ', rel->rd_att->natts * sizeof(char)); + } + + repl[attrdef[i].adnum - 1] = 'r'; + replNull[attrdef[i].adnum - 1] = ' '; + replValue[attrdef[i].adnum - 1] = val; + + } + + pfree(econtext); + + if (repl == NULL) + return (tuple); + + newtuple = heap_modifytuple(tuple, InvalidBuffer, rel, replValue, replNull, repl); + + pfree(repl); + pfree(replNull); + pfree(replValue); + + return (newtuple); + } -static char * -ExecRelCheck (Relation rel, HeapTuple tuple) +static char * +ExecRelCheck(Relation rel, HeapTuple tuple) { - int ncheck = rel->rd_att->constr->num_check; - ConstrCheck *check = rel->rd_att->constr->check; - ExprContext *econtext = makeNode (ExprContext); - TupleTableSlot *slot = makeNode (TupleTableSlot); - RangeTblEntry *rte = makeNode (RangeTblEntry); - List *rtlist; - List *qual; - bool res; - int i; - - slot->val = tuple; - slot->ttc_shouldFree = false; - slot->ttc_descIsNew = true; - slot->ttc_tupleDescriptor = rel->rd_att; - slot->ttc_buffer = InvalidBuffer; - slot->ttc_whichplan = -1; - rte->relname = nameout (&(rel->rd_rel->relname)); - rte->timeRange = NULL; - rte->refname = rte->relname; - rte->relid = rel->rd_id; - rte->inh = false; - rte->archive = false; - rte->inFromCl = true; - rte->timeQual = NULL; - rtlist = lcons (rte, NIL); - econtext->ecxt_scantuple = slot; /* scan tuple slot */ - econtext->ecxt_innertuple = NULL; /* inner tuple slot */ - econtext->ecxt_outertuple = NULL; /* outer tuple slot */ - econtext->ecxt_relation = rel; /* relation */ - econtext->ecxt_relid = 0; /* relid */ - econtext->ecxt_param_list_info = NULL; /* param list info */ - econtext->ecxt_range_table = rtlist; /* range table */ - - for (i = 0; i < ncheck; i++) - { - qual = (List*) stringToNode (check[i].ccbin); - - res = ExecQual (qual, econtext); - - pfree (qual); - - if ( !res ) - return (check[i].ccname); - } - - pfree (slot); - pfree (rte->relname); - pfree (rte); - pfree (rtlist); - pfree (econtext); - - return ((char *) NULL); - + int ncheck = rel->rd_att->constr->num_check; + ConstrCheck *check = rel->rd_att->constr->check; + ExprContext *econtext = makeNode(ExprContext); + TupleTableSlot *slot = makeNode(TupleTableSlot); + RangeTblEntry *rte = makeNode(RangeTblEntry); + List *rtlist; + List *qual; + bool res; + int i; + + slot->val = tuple; + slot->ttc_shouldFree = false; + slot->ttc_descIsNew = true; + slot->ttc_tupleDescriptor = rel->rd_att; + slot->ttc_buffer = InvalidBuffer; + slot->ttc_whichplan = -1; + rte->relname = nameout(&(rel->rd_rel->relname)); + rte->timeRange = NULL; + rte->refname = rte->relname; + rte->relid = rel->rd_id; + rte->inh = false; + rte->archive = false; + rte->inFromCl = true; + rte->timeQual = NULL; + rtlist = lcons(rte, NIL); + econtext->ecxt_scantuple = slot; /* scan tuple slot */ + econtext->ecxt_innertuple = NULL; /* inner tuple slot */ + econtext->ecxt_outertuple = NULL; /* outer tuple slot */ + econtext->ecxt_relation = rel; /* relation */ + econtext->ecxt_relid = 0; /* relid */ + econtext->ecxt_param_list_info = NULL; /* param list info */ + econtext->ecxt_range_table = rtlist; /* range table */ + + for (i = 0; i < ncheck; i++) + { + qual = (List *) stringToNode(check[i].ccbin); + + res = ExecQual(qual, econtext); + + pfree(qual); + + if (!res) + return (check[i].ccname); + } + + pfree(slot); + pfree(rte->relname); + pfree(rte); + pfree(rtlist); + pfree(econtext); + + return ((char *) NULL); + } HeapTuple -ExecConstraints (char *caller, Relation rel, HeapTuple tuple) +ExecConstraints(char *caller, Relation rel, HeapTuple tuple) { - HeapTuple newtuple = tuple; - - Assert ( rel->rd_att->constr ); - - if ( rel->rd_att->constr->num_defval > 0 ) - newtuple = tuple = ExecAttrDefault (rel, tuple); - - if ( rel->rd_att->constr->has_not_null ) - { - int attrChk; - - for (attrChk = 1; attrChk <= rel->rd_att->natts; attrChk++) + HeapTuple newtuple = tuple; + + Assert(rel->rd_att->constr); + + if (rel->rd_att->constr->num_defval > 0) + newtuple = tuple = ExecAttrDefault(rel, tuple); + + if (rel->rd_att->constr->has_not_null) { - if (rel->rd_att->attrs[attrChk-1]->attnotnull && heap_attisnull (tuple,attrChk)) - elog(WARN,"%s: Fail to add null value in not null attribute %s", - caller, rel->rd_att->attrs[attrChk-1]->attname.data); - } - } - - if ( rel->rd_att->constr->num_check > 0 ) - { - char *failed; - - if ( ( failed = ExecRelCheck (rel, tuple) ) != NULL ) - elog(WARN,"%s: rejected due to CHECK constraint %s", caller, failed); - } - - return (newtuple); + int attrChk; + + for (attrChk = 1; attrChk <= rel->rd_att->natts; attrChk++) + { + if (rel->rd_att->attrs[attrChk - 1]->attnotnull && heap_attisnull(tuple, attrChk)) + elog(WARN, "%s: Fail to add null value in not null attribute %s", + caller, rel->rd_att->attrs[attrChk - 1]->attname.data); + } + } + + if (rel->rd_att->constr->num_check > 0) + { + char *failed; + + if ((failed = ExecRelCheck(rel, tuple)) != NULL) + elog(WARN, "%s: rejected due to CHECK constraint %s", caller, failed); + } + + return (newtuple); } diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c index f9e18d948f0..89caefd162e 100644 --- a/src/backend/executor/execProcnode.c +++ b/src/backend/executor/execProcnode.c @@ -1,75 +1,75 @@ /*------------------------------------------------------------------------- * * execProcnode.c-- - * contains dispatch functions which call the appropriate "initialize", - * "get a tuple", and "cleanup" routines for the given node type. - * If the node has children, then it will presumably call ExecInitNode, - * ExecProcNode, or ExecEndNode on it's subnodes and do the appropriate - * processing.. + * contains dispatch functions which call the appropriate "initialize", + * "get a tuple", and "cleanup" routines for the given node type. + * If the node has children, then it will presumably call ExecInitNode, + * ExecProcNode, or ExecEndNode on it's subnodes and do the appropriate + * processing.. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.2 1996/10/31 10:11:27 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.3 1997/09/07 04:41:19 momjian Exp $ * *------------------------------------------------------------------------- */ /* - * INTERFACE ROUTINES - * ExecInitNode - initialize a plan node and it's subplans - * ExecProcNode - get a tuple by executing the plan node - * ExecEndNode - shut down a plan node and it's subplans + * INTERFACE ROUTINES + * ExecInitNode - initialize a plan node and it's subplans + * ExecProcNode - get a tuple by executing the plan node + * ExecEndNode - shut down a plan node and it's subplans * - * NOTES - * This used to be three files. It is now all combined into - * one file so that it is easier to keep ExecInitNode, ExecProcNode, - * and ExecEndNode in sync when new nodes are added. - * - * EXAMPLE - * suppose we want the age of the manager of the shoe department and - * the number of employees in that department. so we have the query: + * NOTES + * This used to be three files. It is now all combined into + * one file so that it is easier to keep ExecInitNode, ExecProcNode, + * and ExecEndNode in sync when new nodes are added. * - * retrieve (DEPT.no_emps, EMP.age) - * where EMP.name = DEPT.mgr and - * DEPT.name = "shoe" - * - * Suppose the planner gives us the following plan: - * - * Nest Loop (DEPT.mgr = EMP.name) - * / \ - * / \ - * Seq Scan Seq Scan - * DEPT EMP - * (name = "shoe") - * - * ExecStart() is called first. - * It calls InitPlan() which calls ExecInitNode() on - * the root of the plan -- the nest loop node. + * EXAMPLE + * suppose we want the age of the manager of the shoe department and + * the number of employees in that department. so we have the query: * - * * ExecInitNode() notices that it is looking at a nest loop and - * as the code below demonstrates, it calls ExecInitNestLoop(). - * Eventually this calls ExecInitNode() on the right and left subplans - * and so forth until the entire plan is initialized. - * - * * Then when ExecRun() is called, it calls ExecutePlan() which - * calls ExecProcNode() repeatedly on the top node of the plan. - * Each time this happens, ExecProcNode() will end up calling - * ExecNestLoop(), which calls ExecProcNode() on its subplans. - * Each of these subplans is a sequential scan so ExecSeqScan() is - * called. The slots returned by ExecSeqScan() may contain - * tuples which contain the attributes ExecNestLoop() uses to - * form the tuples it returns. + * retrieve (DEPT.no_emps, EMP.age) + * where EMP.name = DEPT.mgr and + * DEPT.name = "shoe" * - * * Eventually ExecSeqScan() stops returning tuples and the nest - * loop join ends. Lastly, ExecEnd() calls ExecEndNode() which - * calls ExecEndNestLoop() which in turn calls ExecEndNode() on - * its subplans which result in ExecEndSeqScan(). + * Suppose the planner gives us the following plan: * - * This should show how the executor works by having - * ExecInitNode(), ExecProcNode() and ExecEndNode() dispatch - * their work to the appopriate node support routines which may - * in turn call these routines themselves on their subplans. + * Nest Loop (DEPT.mgr = EMP.name) + * / \ + * / \ + * Seq Scan Seq Scan + * DEPT EMP + * (name = "shoe") + * + * ExecStart() is called first. + * It calls InitPlan() which calls ExecInitNode() on + * the root of the plan -- the nest loop node. + * + * * ExecInitNode() notices that it is looking at a nest loop and + * as the code below demonstrates, it calls ExecInitNestLoop(). + * Eventually this calls ExecInitNode() on the right and left subplans + * and so forth until the entire plan is initialized. + * + * * Then when ExecRun() is called, it calls ExecutePlan() which + * calls ExecProcNode() repeatedly on the top node of the plan. + * Each time this happens, ExecProcNode() will end up calling + * ExecNestLoop(), which calls ExecProcNode() on its subplans. + * Each of these subplans is a sequential scan so ExecSeqScan() is + * called. The slots returned by ExecSeqScan() may contain + * tuples which contain the attributes ExecNestLoop() uses to + * form the tuples it returns. + * + * * Eventually ExecSeqScan() stops returning tuples and the nest + * loop join ends. Lastly, ExecEnd() calls ExecEndNode() which + * calls ExecEndNestLoop() which in turn calls ExecEndNode() on + * its subplans which result in ExecEndSeqScan(). + * + * This should show how the executor works by having + * ExecInitNode(), ExecProcNode() and ExecEndNode() dispatch + * their work to the appopriate node support routines which may + * in turn call these routines themselves on their subplans. * */ #include "postgres.h" @@ -91,389 +91,393 @@ #include "executor/nodeTee.h" /* ------------------------------------------------------------------------ - * ExecInitNode - * - * Recursively initializes all the nodes in the plan rooted - * at 'node'. - * - * Initial States: - * 'node' is the plan produced by the query planner - * - * returns TRUE/FALSE on whether the plan was successfully initialized + * ExecInitNode + * + * Recursively initializes all the nodes in the plan rooted + * at 'node'. + * + * Initial States: + * 'node' is the plan produced by the query planner + * + * returns TRUE/FALSE on whether the plan was successfully initialized * ------------------------------------------------------------------------ */ bool -ExecInitNode(Plan *node, EState *estate, Plan *parent) +ExecInitNode(Plan * node, EState * estate, Plan * parent) { - bool result; - - /* ---------------- - * do nothing when we get to the end - * of a leaf on tree. - * ---------------- - */ - if (node == NULL) - return FALSE; - - switch(nodeTag(node)) { - /* ---------------- - * control nodes - * ---------------- - */ - case T_Result: - result = ExecInitResult((Result *)node, estate, parent); - break; - - case T_Append: - result = ExecInitAppend((Append *)node, estate, parent); - break; - - /* ---------------- - * scan nodes - * ---------------- - */ - case T_SeqScan: - result = ExecInitSeqScan((SeqScan *)node, estate, parent); - break; - - case T_IndexScan: - result = ExecInitIndexScan((IndexScan *)node, estate, parent); - break; - - /* ---------------- - * join nodes - * ---------------- - */ - case T_NestLoop: - result = ExecInitNestLoop((NestLoop *)node, estate, parent); - break; - - case T_MergeJoin: - result = ExecInitMergeJoin((MergeJoin *)node, estate, parent); - break; - + bool result; + /* ---------------- - * materialization nodes + * do nothing when we get to the end + * of a leaf on tree. * ---------------- */ - case T_Material: - result = ExecInitMaterial((Material *)node, estate, parent); - break; - - case T_Sort: - result = ExecInitSort((Sort *)node, estate, parent); - break; - - case T_Unique: - result = ExecInitUnique((Unique *)node, estate, parent); - break; - - case T_Group: - result = ExecInitGroup((Group *)node, estate, parent); - break; - - case T_Agg: - result = ExecInitAgg((Agg *)node, estate, parent); - break; - - case T_Hash: - result = ExecInitHash((Hash *)node, estate, parent); - break; - - case T_HashJoin: - result = ExecInitHashJoin((HashJoin *)node, estate, parent); - break; - - case T_Tee: - result = ExecInitTee((Tee*)node, estate, parent); - break; - - default: - elog(DEBUG, "ExecInitNode: node not yet supported: %d", - nodeTag(node)); - result = FALSE; - } - - return result; + if (node == NULL) + return FALSE; + + switch (nodeTag(node)) + { + /* ---------------- + * control nodes + * ---------------- + */ + case T_Result: + result = ExecInitResult((Result *) node, estate, parent); + break; + + case T_Append: + result = ExecInitAppend((Append *) node, estate, parent); + break; + + /* ---------------- + * scan nodes + * ---------------- + */ + case T_SeqScan: + result = ExecInitSeqScan((SeqScan *) node, estate, parent); + break; + + case T_IndexScan: + result = ExecInitIndexScan((IndexScan *) node, estate, parent); + break; + + /* ---------------- + * join nodes + * ---------------- + */ + case T_NestLoop: + result = ExecInitNestLoop((NestLoop *) node, estate, parent); + break; + + case T_MergeJoin: + result = ExecInitMergeJoin((MergeJoin *) node, estate, parent); + break; + + /* ---------------- + * materialization nodes + * ---------------- + */ + case T_Material: + result = ExecInitMaterial((Material *) node, estate, parent); + break; + + case T_Sort: + result = ExecInitSort((Sort *) node, estate, parent); + break; + + case T_Unique: + result = ExecInitUnique((Unique *) node, estate, parent); + break; + + case T_Group: + result = ExecInitGroup((Group *) node, estate, parent); + break; + + case T_Agg: + result = ExecInitAgg((Agg *) node, estate, parent); + break; + + case T_Hash: + result = ExecInitHash((Hash *) node, estate, parent); + break; + + case T_HashJoin: + result = ExecInitHashJoin((HashJoin *) node, estate, parent); + break; + + case T_Tee: + result = ExecInitTee((Tee *) node, estate, parent); + break; + + default: + elog(DEBUG, "ExecInitNode: node not yet supported: %d", + nodeTag(node)); + result = FALSE; + } + + return result; } /* ---------------------------------------------------------------- - * ExecProcNode - * - * Initial States: - * the query tree must be initialized once by calling ExecInit. + * ExecProcNode + * + * Initial States: + * the query tree must be initialized once by calling ExecInit. * ---------------------------------------------------------------- */ TupleTableSlot * -ExecProcNode(Plan *node, Plan *parent) +ExecProcNode(Plan * node, Plan * parent) { - TupleTableSlot *result; - - /* ---------------- - * deal with NULL nodes.. - * ---------------- - */ - if (node == NULL) - return NULL; - - switch(nodeTag(node)) { - /* ---------------- - * control nodes - * ---------------- - */ - case T_Result: - result = ExecResult((Result *)node); - break; - - case T_Append: - result = ExecProcAppend((Append *)node); - break; - - /* ---------------- - * scan nodes - * ---------------- - */ - case T_SeqScan: - result = ExecSeqScan((SeqScan *)node); - break; - - case T_IndexScan: - result = ExecIndexScan((IndexScan *)node); - break; - - /* ---------------- - * join nodes - * ---------------- - */ - case T_NestLoop: - result = ExecNestLoop((NestLoop *)node, parent); - break; - - case T_MergeJoin: - result = ExecMergeJoin((MergeJoin *)node); - break; - + TupleTableSlot *result; + /* ---------------- - * materialization nodes + * deal with NULL nodes.. * ---------------- */ - case T_Material: - result = ExecMaterial((Material *)node); - break; - - case T_Sort: - result = ExecSort((Sort *)node); - break; - - case T_Unique: - result = ExecUnique((Unique *)node); - break; - - case T_Group: - result = ExecGroup((Group *)node); - break; - - case T_Agg: - result = ExecAgg((Agg *)node); - break; - - case T_Hash: - result = ExecHash((Hash *)node); - break; - - case T_HashJoin: - result = ExecHashJoin((HashJoin *)node); - break; - - case T_Tee: - result = ExecTee((Tee*)node, parent); - break; - - default: - elog(DEBUG, "ExecProcNode: node not yet supported: %d", - nodeTag(node)); - result = FALSE; - } - - return result; + if (node == NULL) + return NULL; + + switch (nodeTag(node)) + { + /* ---------------- + * control nodes + * ---------------- + */ + case T_Result: + result = ExecResult((Result *) node); + break; + + case T_Append: + result = ExecProcAppend((Append *) node); + break; + + /* ---------------- + * scan nodes + * ---------------- + */ + case T_SeqScan: + result = ExecSeqScan((SeqScan *) node); + break; + + case T_IndexScan: + result = ExecIndexScan((IndexScan *) node); + break; + + /* ---------------- + * join nodes + * ---------------- + */ + case T_NestLoop: + result = ExecNestLoop((NestLoop *) node, parent); + break; + + case T_MergeJoin: + result = ExecMergeJoin((MergeJoin *) node); + break; + + /* ---------------- + * materialization nodes + * ---------------- + */ + case T_Material: + result = ExecMaterial((Material *) node); + break; + + case T_Sort: + result = ExecSort((Sort *) node); + break; + + case T_Unique: + result = ExecUnique((Unique *) node); + break; + + case T_Group: + result = ExecGroup((Group *) node); + break; + + case T_Agg: + result = ExecAgg((Agg *) node); + break; + + case T_Hash: + result = ExecHash((Hash *) node); + break; + + case T_HashJoin: + result = ExecHashJoin((HashJoin *) node); + break; + + case T_Tee: + result = ExecTee((Tee *) node, parent); + break; + + default: + elog(DEBUG, "ExecProcNode: node not yet supported: %d", + nodeTag(node)); + result = FALSE; + } + + return result; } int -ExecCountSlotsNode(Plan *node) +ExecCountSlotsNode(Plan * node) { - if (node == (Plan *)NULL) - return 0; - - switch(nodeTag(node)) { - /* ---------------- - * control nodes - * ---------------- - */ - case T_Result: - return ExecCountSlotsResult((Result *)node); - - case T_Append: - return ExecCountSlotsAppend((Append *)node); - - /* ---------------- - * scan nodes - * ---------------- - */ - case T_SeqScan: - return ExecCountSlotsSeqScan((SeqScan *)node); + if (node == (Plan *) NULL) + return 0; - case T_IndexScan: - return ExecCountSlotsIndexScan((IndexScan *)node); - - /* ---------------- - * join nodes - * ---------------- - */ - case T_NestLoop: - return ExecCountSlotsNestLoop((NestLoop *)node); - - case T_MergeJoin: - return ExecCountSlotsMergeJoin((MergeJoin *)node); - - /* ---------------- - * materialization nodes - * ---------------- - */ - case T_Material: - return ExecCountSlotsMaterial((Material *)node); - - case T_Sort: - return ExecCountSlotsSort((Sort *)node); - - case T_Unique: - return ExecCountSlotsUnique((Unique *)node); - - case T_Group: - return ExecCountSlotsGroup((Group *)node); - - case T_Agg: - return ExecCountSlotsAgg((Agg *)node); - - case T_Hash: - return ExecCountSlotsHash((Hash *)node); - - case T_HashJoin: - return ExecCountSlotsHashJoin((HashJoin *)node); - - case T_Tee: - return ExecCountSlotsTee((Tee*)node); - - default: - elog(WARN, "ExecCountSlotsNode: node not yet supported: %d", - nodeTag(node)); - break; - } - return 0; + switch (nodeTag(node)) + { + /* ---------------- + * control nodes + * ---------------- + */ + case T_Result: + return ExecCountSlotsResult((Result *) node); + + case T_Append: + return ExecCountSlotsAppend((Append *) node); + + /* ---------------- + * scan nodes + * ---------------- + */ + case T_SeqScan: + return ExecCountSlotsSeqScan((SeqScan *) node); + + case T_IndexScan: + return ExecCountSlotsIndexScan((IndexScan *) node); + + /* ---------------- + * join nodes + * ---------------- + */ + case T_NestLoop: + return ExecCountSlotsNestLoop((NestLoop *) node); + + case T_MergeJoin: + return ExecCountSlotsMergeJoin((MergeJoin *) node); + + /* ---------------- + * materialization nodes + * ---------------- + */ + case T_Material: + return ExecCountSlotsMaterial((Material *) node); + + case T_Sort: + return ExecCountSlotsSort((Sort *) node); + + case T_Unique: + return ExecCountSlotsUnique((Unique *) node); + + case T_Group: + return ExecCountSlotsGroup((Group *) node); + + case T_Agg: + return ExecCountSlotsAgg((Agg *) node); + + case T_Hash: + return ExecCountSlotsHash((Hash *) node); + + case T_HashJoin: + return ExecCountSlotsHashJoin((HashJoin *) node); + + case T_Tee: + return ExecCountSlotsTee((Tee *) node); + + default: + elog(WARN, "ExecCountSlotsNode: node not yet supported: %d", + nodeTag(node)); + break; + } + return 0; } -/* ---------------------------------------------------------------- - * ExecEndNode - * - * Recursively cleans up all the nodes in the plan rooted - * at 'node'. +/* ---------------------------------------------------------------- + * ExecEndNode * - * After this operation, the query plan will not be able to - * processed any further. This should be called only after - * the query plan has been fully executed. - * ---------------------------------------------------------------- + * Recursively cleans up all the nodes in the plan rooted + * at 'node'. + * + * After this operation, the query plan will not be able to + * processed any further. This should be called only after + * the query plan has been fully executed. + * ---------------------------------------------------------------- */ void -ExecEndNode(Plan *node, Plan *parent) +ExecEndNode(Plan * node, Plan * parent) { - /* ---------------- - * do nothing when we get to the end - * of a leaf on tree. - * ---------------- - */ - if (node == NULL) - return; - - switch(nodeTag(node)) { - /* ---------------- - * control nodes - * ---------------- - */ - case T_Result: - ExecEndResult((Result *)node); - break; - - case T_Append: - ExecEndAppend((Append *)node); - break; - - /* ---------------- - * scan nodes - * ---------------- - */ - case T_SeqScan: - ExecEndSeqScan((SeqScan *)node); - break; - - case T_IndexScan: - ExecEndIndexScan((IndexScan *)node); - break; - /* ---------------- - * join nodes + * do nothing when we get to the end + * of a leaf on tree. * ---------------- */ - case T_NestLoop: - ExecEndNestLoop((NestLoop *)node); - break; - - case T_MergeJoin: - ExecEndMergeJoin((MergeJoin *)node); - break; - - /* ---------------- - * materialization nodes - * ---------------- - */ - case T_Material: - ExecEndMaterial((Material *)node); - break; - - case T_Sort: - ExecEndSort((Sort *)node); - break; - - case T_Unique: - ExecEndUnique((Unique *)node); - break; - - case T_Group: - ExecEndGroup((Group *)node); - break; - - case T_Agg: - ExecEndAgg((Agg *)node); - break; - - /* ---------------- - * XXX add hooks to these - * ---------------- - */ - case T_Hash: - ExecEndHash((Hash *) node); - break; - - case T_HashJoin: - ExecEndHashJoin((HashJoin *) node); - break; - - case T_Tee: - ExecEndTee((Tee*) node, parent); - break; - - default: - elog(DEBUG, "ExecEndNode: node not yet supported", - nodeTag(node)); - break; - } + if (node == NULL) + return; + + switch (nodeTag(node)) + { + /* ---------------- + * control nodes + * ---------------- + */ + case T_Result: + ExecEndResult((Result *) node); + break; + + case T_Append: + ExecEndAppend((Append *) node); + break; + + /* ---------------- + * scan nodes + * ---------------- + */ + case T_SeqScan: + ExecEndSeqScan((SeqScan *) node); + break; + + case T_IndexScan: + ExecEndIndexScan((IndexScan *) node); + break; + + /* ---------------- + * join nodes + * ---------------- + */ + case T_NestLoop: + ExecEndNestLoop((NestLoop *) node); + break; + + case T_MergeJoin: + ExecEndMergeJoin((MergeJoin *) node); + break; + + /* ---------------- + * materialization nodes + * ---------------- + */ + case T_Material: + ExecEndMaterial((Material *) node); + break; + + case T_Sort: + ExecEndSort((Sort *) node); + break; + + case T_Unique: + ExecEndUnique((Unique *) node); + break; + + case T_Group: + ExecEndGroup((Group *) node); + break; + + case T_Agg: + ExecEndAgg((Agg *) node); + break; + + /* ---------------- + * XXX add hooks to these + * ---------------- + */ + case T_Hash: + ExecEndHash((Hash *) node); + break; + + case T_HashJoin: + ExecEndHashJoin((HashJoin *) node); + break; + + case T_Tee: + ExecEndTee((Tee *) node, parent); + break; + + default: + elog(DEBUG, "ExecEndNode: node not yet supported", + nodeTag(node)); + break; + } } diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 536b0068342..7b8cb18ef25 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -1,32 +1,32 @@ /*------------------------------------------------------------------------- * * execQual.c-- - * Routines to evaluate qualification and targetlist expressions + * Routines to evaluate qualification and targetlist expressions * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.12 1997/08/19 21:31:03 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.13 1997/09/07 04:41:20 momjian Exp $ * *------------------------------------------------------------------------- */ /* - * INTERFACE ROUTINES - * ExecEvalExpr - evaluate an expression and return a datum - * ExecQual - return true/false if qualification is satisified - * ExecTargetList - form a new tuple by projecting the given tuple + * INTERFACE ROUTINES + * ExecEvalExpr - evaluate an expression and return a datum + * ExecQual - return true/false if qualification is satisified + * ExecTargetList - form a new tuple by projecting the given tuple * - * NOTES - * ExecEvalExpr() and ExecEvalVar() are hotspots. making these faster - * will speed up the entire system. Unfortunately they are currently - * implemented recursively.. Eliminating the recursion is bound to - * improve the speed of the executor. + * NOTES + * ExecEvalExpr() and ExecEvalVar() are hotspots. making these faster + * will speed up the entire system. Unfortunately they are currently + * implemented recursively.. Eliminating the recursion is bound to + * improve the speed of the executor. * - * ExecTargetList() is used to make tuple projections. Rather then - * trying to speed it up, the execution plan should be pre-processed - * to facilitate attribute sharing between nodes wherever possible, - * instead of doing needless copying. -cim 5/31/91 + * ExecTargetList() is used to make tuple projections. Rather then + * trying to speed it up, the execution plan should be pre-processed + * to facilitate attribute sharing between nodes wherever possible, + * instead of doing needless copying. -cim 5/31/91 * */ #include <string.h> @@ -56,7 +56,7 @@ #include "utils/mcxt.h" /* ---------------- - * externs and constants + * externs and constants * ---------------- */ @@ -65,1492 +65,1577 @@ * Currently only used by ExecHashGetBucket and set only by ExecMakeVarConst * and by ExecEvalArrayRef. */ -bool execConstByVal; -int execConstLen; +bool execConstByVal; +int execConstLen; /* static functions decls */ -static Datum ExecEvalAggreg(Aggreg *agg, ExprContext *econtext, bool *isNull); -static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext, - bool *isNull, bool *isDone); -static Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull); -static Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext, - bool *isNull, bool *isDone); -static void ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext *econtext, - List *argList, Datum argV[], bool *argIsDone); -static Datum ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull); -static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext, - bool *isNull); -static Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull); -static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull); -static Datum ExecMakeFunctionResult(Node *node, List *arguments, - ExprContext *econtext, bool *isNull, bool *isDone); -static bool ExecQualClause(Node *clause, ExprContext *econtext); +static Datum ExecEvalAggreg(Aggreg * agg, ExprContext * econtext, bool * isNull); +static Datum +ExecEvalArrayRef(ArrayRef * arrayRef, ExprContext * econtext, + bool * isNull, bool * isDone); +static Datum ExecEvalAnd(Expr * andExpr, ExprContext * econtext, bool * isNull); +static Datum +ExecEvalFunc(Expr * funcClause, ExprContext * econtext, + bool * isNull, bool * isDone); +static void +ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext * econtext, + List * argList, Datum argV[], bool * argIsDone); +static Datum ExecEvalNot(Expr * notclause, ExprContext * econtext, bool * isNull); +static Datum +ExecEvalOper(Expr * opClause, ExprContext * econtext, + bool * isNull); +static Datum ExecEvalOr(Expr * orExpr, ExprContext * econtext, bool * isNull); +static Datum ExecEvalVar(Var * variable, ExprContext * econtext, bool * isNull); +static Datum +ExecMakeFunctionResult(Node * node, List * arguments, + ExprContext * econtext, bool * isNull, bool * isDone); +static bool ExecQualClause(Node * clause, ExprContext * econtext); /* -------------------------------- - * ExecEvalArrayRef + * ExecEvalArrayRef * - * This function takes an ArrayRef and returns a Const Node if it - * is an array reference or returns the changed Array Node if it is - * an array assignment. + * This function takes an ArrayRef and returns a Const Node if it + * is an array reference or returns the changed Array Node if it is + * an array assignment. * * -------------------------------- */ -static Datum -ExecEvalArrayRef(ArrayRef *arrayRef, - ExprContext *econtext, - bool *isNull, - bool *isDone) +static Datum +ExecEvalArrayRef(ArrayRef * arrayRef, + ExprContext * econtext, + bool * isNull, + bool * isDone) { - bool dummy; - int i = 0, j = 0; - ArrayType *array_scanner; - List *upperIndexpr, *lowerIndexpr; - Node *assgnexpr; - List *elt; - IntArray upper, lower; - int *lIndex; - char *dataPtr; - - *isNull = false; - array_scanner = (ArrayType*)ExecEvalExpr(arrayRef->refexpr, - econtext, - isNull, - isDone); - if (*isNull) return (Datum)NULL; - - upperIndexpr = arrayRef->refupperindexpr; - - foreach (elt, upperIndexpr) { - upper.indx[i++] = (int32)ExecEvalExpr((Node*)lfirst(elt), - econtext, - isNull, - &dummy); - if (*isNull) return (Datum)NULL; - } - - lowerIndexpr = arrayRef->reflowerindexpr; - lIndex = NULL; - if (lowerIndexpr != NIL) { - foreach (elt, lowerIndexpr) { - lower.indx[j++] = (int32)ExecEvalExpr((Node*)lfirst(elt), - econtext, - isNull, - &dummy); - if (*isNull) return (Datum)NULL; + bool dummy; + int i = 0, + j = 0; + ArrayType *array_scanner; + List *upperIndexpr, + *lowerIndexpr; + Node *assgnexpr; + List *elt; + IntArray upper, + lower; + int *lIndex; + char *dataPtr; + + *isNull = false; + array_scanner = (ArrayType *) ExecEvalExpr(arrayRef->refexpr, + econtext, + isNull, + isDone); + if (*isNull) + return (Datum) NULL; + + upperIndexpr = arrayRef->refupperindexpr; + + foreach(elt, upperIndexpr) + { + upper.indx[i++] = (int32) ExecEvalExpr((Node *) lfirst(elt), + econtext, + isNull, + &dummy); + if (*isNull) + return (Datum) NULL; + } + + lowerIndexpr = arrayRef->reflowerindexpr; + lIndex = NULL; + if (lowerIndexpr != NIL) + { + foreach(elt, lowerIndexpr) + { + lower.indx[j++] = (int32) ExecEvalExpr((Node *) lfirst(elt), + econtext, + isNull, + &dummy); + if (*isNull) + return (Datum) NULL; + } + if (i != j) + elog(WARN, + "ExecEvalArrayRef: upper and lower indices mismatch"); + lIndex = lower.indx; } - if (i != j) - elog(WARN, - "ExecEvalArrayRef: upper and lower indices mismatch"); - lIndex = lower.indx; - } - - assgnexpr = arrayRef->refassgnexpr; - if (assgnexpr != NULL) { - dataPtr = (char*)ExecEvalExpr((Node *) - assgnexpr, econtext, - isNull, &dummy); - if (*isNull) return (Datum)NULL; - execConstByVal = arrayRef->refelembyval; - execConstLen = arrayRef->refelemlength; + + assgnexpr = arrayRef->refassgnexpr; + if (assgnexpr != NULL) + { + dataPtr = (char *) ExecEvalExpr((Node *) + assgnexpr, econtext, + isNull, &dummy); + if (*isNull) + return (Datum) NULL; + execConstByVal = arrayRef->refelembyval; + execConstLen = arrayRef->refelemlength; + if (lIndex == NULL) + return (Datum) array_set(array_scanner, i, upper.indx, dataPtr, + arrayRef->refelembyval, + arrayRef->refelemlength, + arrayRef->refattrlength, isNull); + return (Datum) array_assgn(array_scanner, i, upper.indx, + lower.indx, + (ArrayType *) dataPtr, + arrayRef->refelembyval, + arrayRef->refelemlength, isNull); + } + execConstByVal = arrayRef->refelembyval; + execConstLen = arrayRef->refelemlength; if (lIndex == NULL) - return (Datum) array_set(array_scanner, i, upper.indx, dataPtr, - arrayRef->refelembyval, - arrayRef->refelemlength, - arrayRef->refattrlength, isNull); - return (Datum) array_assgn(array_scanner, i, upper.indx, - lower.indx, - (ArrayType*)dataPtr, - arrayRef->refelembyval, - arrayRef->refelemlength, isNull); - } - execConstByVal = arrayRef->refelembyval; - execConstLen = arrayRef->refelemlength; - if (lIndex == NULL) - return (Datum) array_ref(array_scanner, i, upper.indx, - arrayRef->refelembyval, - arrayRef->refelemlength, - arrayRef->refattrlength, isNull); - return (Datum) array_clip(array_scanner, i, upper.indx, lower.indx, - arrayRef->refelembyval, - arrayRef->refelemlength, isNull); + return (Datum) array_ref(array_scanner, i, upper.indx, + arrayRef->refelembyval, + arrayRef->refelemlength, + arrayRef->refattrlength, isNull); + return (Datum) array_clip(array_scanner, i, upper.indx, lower.indx, + arrayRef->refelembyval, + arrayRef->refelemlength, isNull); } /* ---------------------------------------------------------------- - * ExecEvalAggreg - * - * Returns a Datum whose value is the value of the precomputed - * aggregate found in the given expression context. + * ExecEvalAggreg + * + * Returns a Datum whose value is the value of the precomputed + * aggregate found in the given expression context. * ---------------------------------------------------------------- */ -static Datum -ExecEvalAggreg(Aggreg *agg, ExprContext *econtext, bool *isNull) +static Datum +ExecEvalAggreg(Aggreg * agg, ExprContext * econtext, bool * isNull) { - - *isNull = econtext->ecxt_nulls[agg->aggno]; - return econtext->ecxt_values[agg->aggno]; + + *isNull = econtext->ecxt_nulls[agg->aggno]; + return econtext->ecxt_values[agg->aggno]; } /* ---------------------------------------------------------------- - * ExecEvalVar - * - * Returns a Datum whose value is the value of a range - * variable with respect to given expression context. + * ExecEvalVar + * + * Returns a Datum whose value is the value of a range + * variable with respect to given expression context. + * + * + * As an entry condition, we expect that the the datatype the + * plan expects to get (as told by our "variable" argument) is in + * fact the datatype of the attribute the plan says to fetch (as + * seen in the current context, identified by our "econtext" + * argument). + * + * If we fetch a Type A attribute and Caller treats it as if it + * were Type B, there will be undefined results (e.g. crash). + * One way these might mismatch now is that we're accessing a + * catalog class and the type information in the pg_attribute + * class does not match the hardcoded pg_attribute information + * (in pg_attribute.h) for the class in question. * + * We have an Assert to make sure this entry condition is met. * - * As an entry condition, we expect that the the datatype the - * plan expects to get (as told by our "variable" argument) is in - * fact the datatype of the attribute the plan says to fetch (as - * seen in the current context, identified by our "econtext" - * argument). - * - * If we fetch a Type A attribute and Caller treats it as if it - * were Type B, there will be undefined results (e.g. crash). - * One way these might mismatch now is that we're accessing a - * catalog class and the type information in the pg_attribute - * class does not match the hardcoded pg_attribute information - * (in pg_attribute.h) for the class in question. - * - * We have an Assert to make sure this entry condition is met. - * * ---------------------------------------------------------------- */ -static Datum -ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull) +static Datum +ExecEvalVar(Var * variable, ExprContext * econtext, bool * isNull) { - Datum result; - TupleTableSlot *slot; - AttrNumber attnum; - HeapTuple heapTuple; - TupleDesc tuple_type; - Buffer buffer; - bool byval; - int16 len; - - /* ---------------- - * get the slot we want - * ---------------- - */ - switch(variable->varno) { - case INNER: /* get the tuple from the inner node */ - slot = econtext->ecxt_innertuple; - break; - - case OUTER: /* get the tuple from the outer node */ - slot = econtext->ecxt_outertuple; - break; - - default: /* get the tuple from the relation being scanned */ - slot = econtext->ecxt_scantuple; - break; - } - - /* ---------------- - * extract tuple information from the slot - * ---------------- - */ - heapTuple = slot->val; - tuple_type = slot->ttc_tupleDescriptor; - buffer = slot->ttc_buffer; - - attnum = variable->varattno; - - /* (See prolog for explanation of this Assert) */ - Assert(attnum <= 0 || - (attnum - 1 <= tuple_type->natts - 1 && - tuple_type->attrs[attnum-1] != NULL && - variable->vartype == tuple_type->attrs[attnum-1]->atttypid)) - - /* - * If the attribute number is invalid, then we are supposed to - * return the entire tuple, we give back a whole slot so that - * callers know what the tuple looks like. - */ - if (attnum == InvalidAttrNumber) + Datum result; + TupleTableSlot *slot; + AttrNumber attnum; + HeapTuple heapTuple; + TupleDesc tuple_type; + Buffer buffer; + bool byval; + int16 len; + + /* ---------------- + * get the slot we want + * ---------------- + */ + switch (variable->varno) + { + case INNER: /* get the tuple from the inner node */ + slot = econtext->ecxt_innertuple; + break; + + case OUTER: /* get the tuple from the outer node */ + slot = econtext->ecxt_outertuple; + break; + + default: /* get the tuple from the relation being + * scanned */ + slot = econtext->ecxt_scantuple; + break; + } + + /* ---------------- + * extract tuple information from the slot + * ---------------- + */ + heapTuple = slot->val; + tuple_type = slot->ttc_tupleDescriptor; + buffer = slot->ttc_buffer; + + attnum = variable->varattno; + + /* (See prolog for explanation of this Assert) */ + Assert(attnum <= 0 || + (attnum - 1 <= tuple_type->natts - 1 && + tuple_type->attrs[attnum - 1] != NULL && + variable->vartype == tuple_type->attrs[attnum - 1]->atttypid)) + + /* + * If the attribute number is invalid, then we are supposed to return + * the entire tuple, we give back a whole slot so that callers know + * what the tuple looks like. + */ + if (attnum == InvalidAttrNumber) { - TupleTableSlot *tempSlot; - TupleDesc td; - HeapTuple tup; - - tempSlot = makeNode(TupleTableSlot); - tempSlot->ttc_shouldFree = false; - tempSlot->ttc_descIsNew = true; - tempSlot->ttc_tupleDescriptor = (TupleDesc)NULL, - tempSlot->ttc_buffer = InvalidBuffer; - tempSlot->ttc_whichplan = -1; - - tup = heap_copytuple(slot->val); - td = CreateTupleDescCopy(slot->ttc_tupleDescriptor); - - ExecSetSlotDescriptor(tempSlot, td); - - ExecStoreTuple(tup, tempSlot, InvalidBuffer, true); - return (Datum) tempSlot; + TupleTableSlot *tempSlot; + TupleDesc td; + HeapTuple tup; + + tempSlot = makeNode(TupleTableSlot); + tempSlot->ttc_shouldFree = false; + tempSlot->ttc_descIsNew = true; + tempSlot->ttc_tupleDescriptor = (TupleDesc) NULL, + tempSlot->ttc_buffer = InvalidBuffer; + tempSlot->ttc_whichplan = -1; + + tup = heap_copytuple(slot->val); + td = CreateTupleDescCopy(slot->ttc_tupleDescriptor); + + ExecSetSlotDescriptor(tempSlot, td); + + ExecStoreTuple(tup, tempSlot, InvalidBuffer, true); + return (Datum) tempSlot; } - - result = (Datum) - heap_getattr(heapTuple, /* tuple containing attribute */ - buffer, /* buffer associated with tuple */ - attnum, /* attribute number of desired attribute */ - tuple_type, /* tuple descriptor of tuple */ - isNull); /* return: is attribute null? */ - - /* ---------------- - * return null if att is null - * ---------------- - */ - if (*isNull) - return (Datum) NULL; - - /* ---------------- - * get length and type information.. - * ??? what should we do about variable length attributes - * - variable length attributes have their length stored - * in the first 4 bytes of the memory pointed to by the - * returned value.. If we can determine that the type - * is a variable length type, we can do the right thing. - * -cim 9/15/89 - * ---------------- - */ - if (attnum < 0) { + + result = (Datum) + heap_getattr(heapTuple, /* tuple containing attribute */ + buffer, /* buffer associated with tuple */ + attnum, /* attribute number of desired attribute */ + tuple_type,/* tuple descriptor of tuple */ + isNull); /* return: is attribute null? */ + /* ---------------- - * If this is a pseudo-att, we get the type and fake the length. - * There ought to be a routine to return the real lengths, so - * we'll mark this one ... XXX -mao + * return null if att is null * ---------------- */ - len = heap_sysattrlen(attnum); /* XXX see -mao above */ - byval = heap_sysattrbyval(attnum); /* XXX see -mao above */ - } else { - len = tuple_type->attrs[ attnum-1 ]->attlen; - byval = tuple_type->attrs[ attnum-1 ]->attbyval ? true : false ; - } - - execConstByVal = byval; - execConstLen = len; - - return result; + if (*isNull) + return (Datum) NULL; + + /* ---------------- + * get length and type information.. + * ??? what should we do about variable length attributes + * - variable length attributes have their length stored + * in the first 4 bytes of the memory pointed to by the + * returned value.. If we can determine that the type + * is a variable length type, we can do the right thing. + * -cim 9/15/89 + * ---------------- + */ + if (attnum < 0) + { + /* ---------------- + * If this is a pseudo-att, we get the type and fake the length. + * There ought to be a routine to return the real lengths, so + * we'll mark this one ... XXX -mao + * ---------------- + */ + len = heap_sysattrlen(attnum); /* XXX see -mao above */ + byval = heap_sysattrbyval(attnum); /* XXX see -mao above */ + } + else + { + len = tuple_type->attrs[attnum - 1]->attlen; + byval = tuple_type->attrs[attnum - 1]->attbyval ? true : false; + } + + execConstByVal = byval; + execConstLen = len; + + return result; } /* ---------------------------------------------------------------- - * ExecEvalParam + * ExecEvalParam * - * Returns the value of a parameter. A param node contains - * something like ($.name) and the expression context contains - * the current parameter bindings (name = "sam") (age = 34)... - * so our job is to replace the param node with the datum - * containing the appropriate information ("sam"). + * Returns the value of a parameter. A param node contains + * something like ($.name) and the expression context contains + * the current parameter bindings (name = "sam") (age = 34)... + * so our job is to replace the param node with the datum + * containing the appropriate information ("sam"). * - * Q: if we have a parameter ($.foo) without a binding, i.e. - * there is no (foo = xxx) in the parameter list info, - * is this a fatal error or should this be a "not available" - * (in which case we shoud return a Const node with the - * isnull flag) ? -cim 10/13/89 + * Q: if we have a parameter ($.foo) without a binding, i.e. + * there is no (foo = xxx) in the parameter list info, + * is this a fatal error or should this be a "not available" + * (in which case we shoud return a Const node with the + * isnull flag) ? -cim 10/13/89 * - * Minor modification: Param nodes now have an extra field, - * `paramkind' which specifies the type of parameter - * (see params.h). So while searching the paramList for - * a paramname/value pair, we have also to check for `kind'. - * - * NOTE: The last entry in `paramList' is always an - * entry with kind == PARAM_INVALID. + * Minor modification: Param nodes now have an extra field, + * `paramkind' which specifies the type of parameter + * (see params.h). So while searching the paramList for + * a paramname/value pair, we have also to check for `kind'. + * + * NOTE: The last entry in `paramList' is always an + * entry with kind == PARAM_INVALID. * ---------------------------------------------------------------- */ Datum -ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull) +ExecEvalParam(Param * expression, ExprContext * econtext, bool * isNull) { - - char *thisParameterName; - int thisParameterKind; - AttrNumber thisParameterId; - int matchFound; - ParamListInfo paramList; - - thisParameterName = expression->paramname; - thisParameterKind = expression->paramkind; - thisParameterId = expression->paramid; - paramList = econtext->ecxt_param_list_info; - - *isNull = false; - /* - * search the list with the parameter info to find a matching name. - * An entry with an InvalidName denotes the last element in the array. - */ - matchFound = 0; - if (paramList != NULL) { + + char *thisParameterName; + int thisParameterKind; + AttrNumber thisParameterId; + int matchFound; + ParamListInfo paramList; + + thisParameterName = expression->paramname; + thisParameterKind = expression->paramkind; + thisParameterId = expression->paramid; + paramList = econtext->ecxt_param_list_info; + + *isNull = false; + /* - * search for an entry in 'paramList' that matches - * the `expression'. + * search the list with the parameter info to find a matching name. An + * entry with an InvalidName denotes the last element in the array. */ - while(paramList->kind != PARAM_INVALID && !matchFound) { - switch (thisParameterKind) { - case PARAM_NAMED: - if (thisParameterKind == paramList->kind && - strcmp(paramList->name, thisParameterName) == 0){ - matchFound = 1; - } - break; - case PARAM_NUM: - if (thisParameterKind == paramList->kind && - paramList->id == thisParameterId) { - matchFound = 1; - } - break; - case PARAM_OLD: - case PARAM_NEW: - if (thisParameterKind == paramList->kind && - paramList->id == thisParameterId) - { - matchFound = 1; - /* - * sanity check - */ - if (strcmp(paramList->name, thisParameterName) != 0){ - elog(WARN, - "ExecEvalParam: new/old params with same id & diff names"); + matchFound = 0; + if (paramList != NULL) + { + + /* + * search for an entry in 'paramList' that matches the + * `expression'. + */ + while (paramList->kind != PARAM_INVALID && !matchFound) + { + switch (thisParameterKind) + { + case PARAM_NAMED: + if (thisParameterKind == paramList->kind && + strcmp(paramList->name, thisParameterName) == 0) + { + matchFound = 1; + } + break; + case PARAM_NUM: + if (thisParameterKind == paramList->kind && + paramList->id == thisParameterId) + { + matchFound = 1; + } + break; + case PARAM_OLD: + case PARAM_NEW: + if (thisParameterKind == paramList->kind && + paramList->id == thisParameterId) + { + matchFound = 1; + + /* + * sanity check + */ + if (strcmp(paramList->name, thisParameterName) != 0) + { + elog(WARN, + "ExecEvalParam: new/old params with same id & diff names"); + } + } + break; + default: + + /* + * oops! this is not supposed to happen! + */ + elog(WARN, "ExecEvalParam: invalid paramkind %d", + thisParameterKind); } - } - break; - default: + if (!matchFound) + { + paramList++; + } + } /* while */ + } /* if */ + + if (!matchFound) + { + /* - * oops! this is not supposed to happen! + * ooops! we couldn't find this parameter in the parameter list. + * Signal an error */ - elog(WARN, "ExecEvalParam: invalid paramkind %d", - thisParameterKind); - } - if (! matchFound) { - paramList++; - } - } /*while*/ - } /*if*/ - - if (!matchFound) { + elog(WARN, "ExecEvalParam: Unknown value for parameter %s", + thisParameterName); + } + /* - * ooops! we couldn't find this parameter - * in the parameter list. Signal an error + * return the value. */ - elog(WARN, "ExecEvalParam: Unknown value for parameter %s", - thisParameterName); - } - - /* - * return the value. - */ - if (paramList->isnull) + if (paramList->isnull) { - *isNull = true; - return (Datum)NULL; + *isNull = true; + return (Datum) NULL; } - - if (expression->param_tlist != NIL) + + if (expression->param_tlist != NIL) { - HeapTuple tup; - Datum value; - List *tlist = expression->param_tlist; - TargetEntry *tle = (TargetEntry *)lfirst(tlist); - TupleTableSlot *slot = (TupleTableSlot *)paramList->value; - - tup = slot->val; - value = ProjectAttribute(slot->ttc_tupleDescriptor, - tle, tup, isNull); - return value; + HeapTuple tup; + Datum value; + List *tlist = expression->param_tlist; + TargetEntry *tle = (TargetEntry *) lfirst(tlist); + TupleTableSlot *slot = (TupleTableSlot *) paramList->value; + + tup = slot->val; + value = ProjectAttribute(slot->ttc_tupleDescriptor, + tle, tup, isNull); + return value; } - return(paramList->value); + return (paramList->value); } /* ---------------------------------------------------------------- - * ExecEvalOper / ExecEvalFunc support routines + * ExecEvalOper / ExecEvalFunc support routines * ---------------------------------------------------------------- */ /* ---------------- - * GetAttributeByName - * GetAttributeByNum + * GetAttributeByName + * GetAttributeByNum * - * These are functions which return the value of the - * named attribute out of the tuple from the arg slot. User defined - * C functions which take a tuple as an argument are expected - * to use this. Ex: overpaid(EMP) might call GetAttributeByNum(). + * These are functions which return the value of the + * named attribute out of the tuple from the arg slot. User defined + * C functions which take a tuple as an argument are expected + * to use this. Ex: overpaid(EMP) might call GetAttributeByNum(). * ---------------- */ #ifdef NOT_USED -static char * -GetAttributeByNum(TupleTableSlot *slot, - AttrNumber attrno, - bool *isNull) +static char * +GetAttributeByNum(TupleTableSlot * slot, + AttrNumber attrno, + bool * isNull) { - Datum retval; - - if (!AttributeNumberIsValid(attrno)) - elog(WARN, "GetAttributeByNum: Invalid attribute number"); - - if (!AttrNumberIsForUserDefinedAttr(attrno)) - elog(WARN, "GetAttributeByNum: cannot access system attributes here"); - - if (isNull == (bool *)NULL) - elog(WARN, "GetAttributeByNum: a NULL isNull flag was passed"); - - if (TupIsNull(slot)) + Datum retval; + + if (!AttributeNumberIsValid(attrno)) + elog(WARN, "GetAttributeByNum: Invalid attribute number"); + + if (!AttrNumberIsForUserDefinedAttr(attrno)) + elog(WARN, "GetAttributeByNum: cannot access system attributes here"); + + if (isNull == (bool *) NULL) + elog(WARN, "GetAttributeByNum: a NULL isNull flag was passed"); + + if (TupIsNull(slot)) { - *isNull = true; - return (char *) NULL; + *isNull = true; + return (char *) NULL; } - - retval = (Datum) - heap_getattr(slot->val, - slot->ttc_buffer, - attrno, - slot->ttc_tupleDescriptor, - isNull); - if (*isNull) - return (char *) NULL; - return (char *) retval; + + retval = (Datum) + heap_getattr(slot->val, + slot->ttc_buffer, + attrno, + slot->ttc_tupleDescriptor, + isNull); + if (*isNull) + return (char *) NULL; + return (char *) retval; } + #endif /* XXX char16 name for catalogs */ #ifdef NOT_USED -char * -att_by_num(TupleTableSlot *slot, - AttrNumber attrno, - bool *isNull) +char * +att_by_num(TupleTableSlot * slot, + AttrNumber attrno, + bool * isNull) { - return(GetAttributeByNum(slot, attrno, isNull)); + return (GetAttributeByNum(slot, attrno, isNull)); } + #endif -char * -GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull) +char * +GetAttributeByName(TupleTableSlot * slot, char *attname, bool * isNull) { - AttrNumber attrno; - TupleDesc tupdesc; - HeapTuple tuple; - Datum retval; - int natts; - int i; - - if (attname == NULL) - elog(WARN, "GetAttributeByName: Invalid attribute name"); - - if (isNull == (bool *)NULL) - elog(WARN, "GetAttributeByName: a NULL isNull flag was passed"); - - if (TupIsNull(slot)) + AttrNumber attrno; + TupleDesc tupdesc; + HeapTuple tuple; + Datum retval; + int natts; + int i; + + if (attname == NULL) + elog(WARN, "GetAttributeByName: Invalid attribute name"); + + if (isNull == (bool *) NULL) + elog(WARN, "GetAttributeByName: a NULL isNull flag was passed"); + + if (TupIsNull(slot)) { - *isNull = true; - return (char *) NULL; + *isNull = true; + return (char *) NULL; } - - tupdesc = slot->ttc_tupleDescriptor; - tuple = slot->val; - - natts = tuple->t_natts; - - attrno = InvalidAttrNumber; - for (i=0;i<tupdesc->natts;i++) { - if (namestrcmp(&(tupdesc->attrs[i]->attname), attname) == 0) { - attrno = tupdesc->attrs[i]->attnum; - break; + + tupdesc = slot->ttc_tupleDescriptor; + tuple = slot->val; + + natts = tuple->t_natts; + + attrno = InvalidAttrNumber; + for (i = 0; i < tupdesc->natts; i++) + { + if (namestrcmp(&(tupdesc->attrs[i]->attname), attname) == 0) + { + attrno = tupdesc->attrs[i]->attnum; + break; + } } - } - - if (attrno == InvalidAttrNumber) - elog(WARN, "GetAttributeByName: attribute %s not found", attname); - - retval = (Datum) - heap_getattr(slot->val, - slot->ttc_buffer, - attrno, - tupdesc, - isNull); - if (*isNull) - return (char *) NULL; - return (char *) retval; + + if (attrno == InvalidAttrNumber) + elog(WARN, "GetAttributeByName: attribute %s not found", attname); + + retval = (Datum) + heap_getattr(slot->val, + slot->ttc_buffer, + attrno, + tupdesc, + isNull); + if (*isNull) + return (char *) NULL; + return (char *) retval; } /* XXX char16 name for catalogs */ #ifdef NOT_USED -char * -att_by_name(TupleTableSlot *slot, char *attname, bool *isNull) +char * +att_by_name(TupleTableSlot * slot, char *attname, bool * isNull) { - return(GetAttributeByName(slot, attname, isNull)); + return (GetAttributeByName(slot, attname, isNull)); } + #endif static void ExecEvalFuncArgs(FunctionCachePtr fcache, - ExprContext *econtext, - List *argList, - Datum argV[], - bool *argIsDone) + ExprContext * econtext, + List * argList, + Datum argV[], + bool * argIsDone) { - int i; - bool argIsNull, *nullVect; - List *arg; - - nullVect = fcache->nullVect; - - i = 0; - foreach (arg, argList) { - /* ---------------- - * evaluate the expression, in general functions cannot take - * sets as arguments but we make an exception in the case of - * nested dot expressions. We have to watch out for this case - * here. - * ---------------- - */ - argV[i] = (Datum) - ExecEvalExpr((Node *) lfirst(arg), - econtext, - &argIsNull, - argIsDone); - if (! (*argIsDone)) - { - Assert(i == 0); - fcache->setArg = (char *)argV[0]; - fcache->hasSetArg = true; - } - if (argIsNull) - nullVect[i] = true; - else - nullVect[i] = false; - i++; - } + int i; + bool argIsNull, + *nullVect; + List *arg; + + nullVect = fcache->nullVect; + + i = 0; + foreach(arg, argList) + { + /* ---------------- + * evaluate the expression, in general functions cannot take + * sets as arguments but we make an exception in the case of + * nested dot expressions. We have to watch out for this case + * here. + * ---------------- + */ + argV[i] = (Datum) + ExecEvalExpr((Node *) lfirst(arg), + econtext, + &argIsNull, + argIsDone); + if (!(*argIsDone)) + { + Assert(i == 0); + fcache->setArg = (char *) argV[0]; + fcache->hasSetArg = true; + } + if (argIsNull) + nullVect[i] = true; + else + nullVect[i] = false; + i++; + } } /* ---------------- - * ExecMakeFunctionResult + * ExecMakeFunctionResult * ---------------- */ -static Datum -ExecMakeFunctionResult(Node *node, - List *arguments, - ExprContext *econtext, - bool *isNull, - bool *isDone) +static Datum +ExecMakeFunctionResult(Node * node, + List * arguments, + ExprContext * econtext, + bool * isNull, + bool * isDone) { - Datum argv[MAXFMGRARGS]; - FunctionCachePtr fcache; - Func *funcNode = NULL; - Oper *operNode = NULL; - bool funcisset = false; - - /* - * This is kind of ugly, Func nodes now have targetlists so that - * we know when and what to project out from postquel function results. - * This means we have to pass the func node all the way down instead - * of using only the fcache struct as before. ExecMakeFunctionResult - * becomes a little bit more of a dual personality as a result. - */ - if (IsA(node,Func)) + Datum argv[MAXFMGRARGS]; + FunctionCachePtr fcache; + Func *funcNode = NULL; + Oper *operNode = NULL; + bool funcisset = false; + + /* + * This is kind of ugly, Func nodes now have targetlists so that we + * know when and what to project out from postquel function results. + * This means we have to pass the func node all the way down instead + * of using only the fcache struct as before. ExecMakeFunctionResult + * becomes a little bit more of a dual personality as a result. + */ + if (IsA(node, Func)) { - funcNode = (Func *)node; - fcache = funcNode->func_fcache; + funcNode = (Func *) node; + fcache = funcNode->func_fcache; } - else + else { - operNode = (Oper *)node; - fcache = operNode->op_fcache; + operNode = (Oper *) node; + fcache = operNode->op_fcache; } - - /* ---------------- - * arguments is a list of expressions to evaluate - * before passing to the function manager. - * We collect the results of evaluating the expressions - * into a datum array (argv) and pass this array to arrayFmgr() - * ---------------- - */ - if (fcache->nargs != 0) { - bool argDone; - - if (fcache->nargs > MAXFMGRARGS) - elog(WARN, "ExecMakeFunctionResult: too many arguments"); - - /* - * If the setArg in the fcache is set we have an argument - * returning a set of tuples (i.e. a nested dot expression). We - * don't want to evaluate the arguments again until the function - * is done. hasSetArg will always be false until we eval the args - * for the first time. We should set this in the parser. + + /* ---------------- + * arguments is a list of expressions to evaluate + * before passing to the function manager. + * We collect the results of evaluating the expressions + * into a datum array (argv) and pass this array to arrayFmgr() + * ---------------- */ - if ((fcache->hasSetArg) && fcache->setArg != NULL) - { - argv[0] = (Datum)fcache->setArg; - argDone = false; - } - else - ExecEvalFuncArgs(fcache, econtext, arguments, argv, &argDone); - - if ((fcache->hasSetArg) && (argDone)) { - if (isDone) *isDone = true; - return (Datum)NULL; - } - } - - /* If this function is really a set, we have to diddle with things. - * If the function has already been called at least once, then the - * setArg field of the fcache holds - * the OID of this set in pg_proc. (This is not quite legit, since - * the setArg field is really for functions which take sets of tuples - * as input - set functions take no inputs at all. But it's a nice - * place to stash this value, for now.) - * - * If this is the first call of the set's function, then - * the call to ExecEvalFuncArgs above just returned the OID of - * the pg_proc tuple which defines this set. So replace the existing - * funcid in the funcnode with the set's OID. Also, we want a new - * fcache which points to the right function, so get that, now that - * we have the right OID. Also zero out the argv, since the real - * set doesn't take any arguments. - */ - if (((Func *)node)->funcid == SetEvalRegProcedure) { - funcisset = true; - if (fcache->setArg) { - argv[0] = 0; - - ((Func *)node)->funcid = (Oid) PointerGetDatum(fcache->setArg); - - } else { - ((Func *)node)->funcid = (Oid) argv[0]; - setFcache(node, argv[0], NIL,econtext); - fcache = ((Func *)node)->func_fcache; - fcache->setArg = (char*)argv[0]; - argv[0] = (Datum)0; + if (fcache->nargs != 0) + { + bool argDone; + + if (fcache->nargs > MAXFMGRARGS) + elog(WARN, "ExecMakeFunctionResult: too many arguments"); + + /* + * If the setArg in the fcache is set we have an argument + * returning a set of tuples (i.e. a nested dot expression). We + * don't want to evaluate the arguments again until the function + * is done. hasSetArg will always be false until we eval the args + * for the first time. We should set this in the parser. + */ + if ((fcache->hasSetArg) && fcache->setArg != NULL) + { + argv[0] = (Datum) fcache->setArg; + argDone = false; + } + else + ExecEvalFuncArgs(fcache, econtext, arguments, argv, &argDone); + + if ((fcache->hasSetArg) && (argDone)) + { + if (isDone) + *isDone = true; + return (Datum) NULL; + } } - } - - /* ---------------- - * now return the value gotten by calling the function manager, - * passing the function the evaluated parameter values. - * ---------------- - */ - if (fcache->language == SQLlanguageId) { - Datum result; - - Assert(funcNode); - result = postquel_function (funcNode, (char **) argv, isNull, isDone); + /* - * finagle the situation where we are iterating through all results - * in a nested dot function (whose argument function returns a set - * of tuples) and the current function finally finishes. We need to - * get the next argument in the set and run the function all over - * again. This is getting unclean. + * If this function is really a set, we have to diddle with things. If + * the function has already been called at least once, then the setArg + * field of the fcache holds the OID of this set in pg_proc. (This is + * not quite legit, since the setArg field is really for functions + * which take sets of tuples as input - set functions take no inputs + * at all. But it's a nice place to stash this value, for now.) + * + * If this is the first call of the set's function, then the call to + * ExecEvalFuncArgs above just returned the OID of the pg_proc tuple + * which defines this set. So replace the existing funcid in the + * funcnode with the set's OID. Also, we want a new fcache which + * points to the right function, so get that, now that we have the + * right OID. Also zero out the argv, since the real set doesn't take + * any arguments. */ - if ((*isDone) && (fcache->hasSetArg)) { - bool argDone; - - ExecEvalFuncArgs(fcache, econtext, arguments, argv, &argDone); - - if (argDone) { - fcache->setArg = (char *)NULL; - *isDone = true; - result = (Datum)NULL; - } - else - result = postquel_function(funcNode, - (char **) argv, - isNull, - isDone); + if (((Func *) node)->funcid == SetEvalRegProcedure) + { + funcisset = true; + if (fcache->setArg) + { + argv[0] = 0; + + ((Func *) node)->funcid = (Oid) PointerGetDatum(fcache->setArg); + + } + else + { + ((Func *) node)->funcid = (Oid) argv[0]; + setFcache(node, argv[0], NIL, econtext); + fcache = ((Func *) node)->func_fcache; + fcache->setArg = (char *) argv[0]; + argv[0] = (Datum) 0; + } } - if (funcisset) { - /* reset the funcid so that next call to this routine will - * still recognize this func as a set. - * Note that for now we assume that the set function in - * pg_proc must be a Postquel function - the funcid is - * not reset below for C functions. - */ - ((Func *)node)->funcid = SetEvalRegProcedure; - /* If we're done with the results of this function, get rid - * of its func cache. - */ - if (*isDone) { - ((Func *)node)->func_fcache = NULL; - } + + /* ---------------- + * now return the value gotten by calling the function manager, + * passing the function the evaluated parameter values. + * ---------------- + */ + if (fcache->language == SQLlanguageId) + { + Datum result; + + Assert(funcNode); + result = postquel_function(funcNode, (char **) argv, isNull, isDone); + + /* + * finagle the situation where we are iterating through all + * results in a nested dot function (whose argument function + * returns a set of tuples) and the current function finally + * finishes. We need to get the next argument in the set and run + * the function all over again. This is getting unclean. + */ + if ((*isDone) && (fcache->hasSetArg)) + { + bool argDone; + + ExecEvalFuncArgs(fcache, econtext, arguments, argv, &argDone); + + if (argDone) + { + fcache->setArg = (char *) NULL; + *isDone = true; + result = (Datum) NULL; + } + else + result = postquel_function(funcNode, + (char **) argv, + isNull, + isDone); + } + if (funcisset) + { + + /* + * reset the funcid so that next call to this routine will + * still recognize this func as a set. Note that for now we + * assume that the set function in pg_proc must be a Postquel + * function - the funcid is not reset below for C functions. + */ + ((Func *) node)->funcid = SetEvalRegProcedure; + + /* + * If we're done with the results of this function, get rid of + * its func cache. + */ + if (*isDone) + { + ((Func *) node)->func_fcache = NULL; + } + } + return result; } - return result; - } - else + else { - int i; - - if (isDone) *isDone = true; - for (i = 0; i < fcache->nargs; i++) - if (fcache->nullVect[i] == true) *isNull = true; - - return((Datum) fmgr_c(fcache->func, fcache->foid, fcache->nargs, - (FmgrValues *) argv, isNull)); + int i; + + if (isDone) + *isDone = true; + for (i = 0; i < fcache->nargs; i++) + if (fcache->nullVect[i] == true) + *isNull = true; + + return ((Datum) fmgr_c(fcache->func, fcache->foid, fcache->nargs, + (FmgrValues *) argv, isNull)); } } /* ---------------------------------------------------------------- - * ExecEvalOper - * ExecEvalFunc - * - * Evaluate the functional result of a list of arguments by calling the - * function manager. Note that in the case of operator expressions, the - * optimizer had better have already replaced the operator OID with the - * appropriate function OID or we're hosed. + * ExecEvalOper + * ExecEvalFunc + * + * Evaluate the functional result of a list of arguments by calling the + * function manager. Note that in the case of operator expressions, the + * optimizer had better have already replaced the operator OID with the + * appropriate function OID or we're hosed. * * old comments - * Presumably the function manager will not take null arguments, so we - * check for null arguments before sending the arguments to (fmgr). - * - * Returns the value of the functional expression. + * Presumably the function manager will not take null arguments, so we + * check for null arguments before sending the arguments to (fmgr). + * + * Returns the value of the functional expression. * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- - * ExecEvalOper + * ExecEvalOper * ---------------------------------------------------------------- */ -static Datum -ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull) +static Datum +ExecEvalOper(Expr * opClause, ExprContext * econtext, bool * isNull) { - Oper *op; - List *argList; - FunctionCachePtr fcache; - bool isDone; - - /* ---------------- - * an opclause is a list (op args). (I think) - * - * we extract the oid of the function associated with - * the op and then pass the work onto ExecMakeFunctionResult - * which evaluates the arguments and returns the result of - * calling the function on the evaluated arguments. - * ---------------- - */ - op = (Oper *) opClause->oper; - argList = opClause->args; - - /* - * get the fcache from the Oper node. - * If it is NULL, then initialize it - */ - fcache = op->op_fcache; - if (fcache == NULL) { - setFcache((Node*)op, op->opid, argList, econtext); - fcache = op->op_fcache; - } - - /* ----------- - * call ExecMakeFunctionResult() with a dummy isDone that we ignore. - * We don't have operator whose arguments are sets. - * ----------- - */ - return - ExecMakeFunctionResult((Node *)op, argList, econtext, isNull, &isDone); + Oper *op; + List *argList; + FunctionCachePtr fcache; + bool isDone; + + /* ---------------- + * an opclause is a list (op args). (I think) + * + * we extract the oid of the function associated with + * the op and then pass the work onto ExecMakeFunctionResult + * which evaluates the arguments and returns the result of + * calling the function on the evaluated arguments. + * ---------------- + */ + op = (Oper *) opClause->oper; + argList = opClause->args; + + /* + * get the fcache from the Oper node. If it is NULL, then initialize + * it + */ + fcache = op->op_fcache; + if (fcache == NULL) + { + setFcache((Node *) op, op->opid, argList, econtext); + fcache = op->op_fcache; + } + + /* ----------- + * call ExecMakeFunctionResult() with a dummy isDone that we ignore. + * We don't have operator whose arguments are sets. + * ----------- + */ + return + ExecMakeFunctionResult((Node *) op, argList, econtext, isNull, &isDone); } /* ---------------------------------------------------------------- - * ExecEvalFunc + * ExecEvalFunc * ---------------------------------------------------------------- */ -static Datum -ExecEvalFunc(Expr *funcClause, - ExprContext *econtext, - bool *isNull, - bool *isDone) +static Datum +ExecEvalFunc(Expr * funcClause, + ExprContext * econtext, + bool * isNull, + bool * isDone) { - Func *func; - List *argList; - FunctionCachePtr fcache; - - /* ---------------- - * an funcclause is a list (func args). (I think) - * - * we extract the oid of the function associated with - * the func node and then pass the work onto ExecMakeFunctionResult - * which evaluates the arguments and returns the result of - * calling the function on the evaluated arguments. - * - * this is nearly identical to the ExecEvalOper code. - * ---------------- - */ - func = (Func *)funcClause->oper; - argList = funcClause->args; - - /* - * get the fcache from the Func node. - * If it is NULL, then initialize it - */ - fcache = func->func_fcache; - if (fcache == NULL) { - setFcache((Node*)func, func->funcid, argList, econtext); - fcache = func->func_fcache; - } - - return - ExecMakeFunctionResult((Node*)func, argList, econtext, isNull, isDone); + Func *func; + List *argList; + FunctionCachePtr fcache; + + /* ---------------- + * an funcclause is a list (func args). (I think) + * + * we extract the oid of the function associated with + * the func node and then pass the work onto ExecMakeFunctionResult + * which evaluates the arguments and returns the result of + * calling the function on the evaluated arguments. + * + * this is nearly identical to the ExecEvalOper code. + * ---------------- + */ + func = (Func *) funcClause->oper; + argList = funcClause->args; + + /* + * get the fcache from the Func node. If it is NULL, then initialize + * it + */ + fcache = func->func_fcache; + if (fcache == NULL) + { + setFcache((Node *) func, func->funcid, argList, econtext); + fcache = func->func_fcache; + } + + return + ExecMakeFunctionResult((Node *) func, argList, econtext, isNull, isDone); } /* ---------------------------------------------------------------- - * ExecEvalNot - * ExecEvalOr - * ExecEvalAnd - * - * Evaluate boolean expressions. Evaluation of 'or' is - * short-circuited when the first true (or null) value is found. + * ExecEvalNot + * ExecEvalOr + * ExecEvalAnd * - * The query planner reformulates clause expressions in the - * qualification to conjunctive normal form. If we ever get - * an AND to evaluate, we can be sure that it's not a top-level - * clause in the qualification, but appears lower (as a function - * argument, for example), or in the target list. Not that you - * need to know this, mind you... + * Evaluate boolean expressions. Evaluation of 'or' is + * short-circuited when the first true (or null) value is found. + * + * The query planner reformulates clause expressions in the + * qualification to conjunctive normal form. If we ever get + * an AND to evaluate, we can be sure that it's not a top-level + * clause in the qualification, but appears lower (as a function + * argument, for example), or in the target list. Not that you + * need to know this, mind you... * ---------------------------------------------------------------- */ -static Datum -ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull) +static Datum +ExecEvalNot(Expr * notclause, ExprContext * econtext, bool * isNull) { - Datum expr_value; - Node *clause; - bool isDone; - - clause = lfirst(notclause->args); - - /* ---------------- - * We don't iterate over sets in the quals, so pass in an isDone - * flag, but ignore it. - * ---------------- - */ - expr_value = ExecEvalExpr(clause, econtext, isNull, &isDone); - - /* ---------------- - * if the expression evaluates to null, then we just - * cascade the null back to whoever called us. - * ---------------- - */ - if (*isNull) - return expr_value; - - /* ---------------- - * evaluation of 'not' is simple.. expr is false, then - * return 'true' and vice versa. - * ---------------- - */ - if (DatumGetInt32(expr_value) == 0) - return (Datum) true; - - return (Datum) false; -} + Datum expr_value; + Node *clause; + bool isDone; + + clause = lfirst(notclause->args); -/* ---------------------------------------------------------------- - * ExecEvalOr - * ---------------------------------------------------------------- - */ -static Datum -ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull) -{ - List *clauses; - List *clause; - bool isDone; - bool IsNull; - Datum const_value = 0; - - IsNull = false; - clauses = orExpr->args; - - /* ---------------- - * we use three valued logic functions here... - * we evaluate each of the clauses in turn, - * as soon as one is true we return that - * value. If none is true and none of the - * clauses evaluate to NULL we return - * the value of the last clause evaluated (which - * should be false) with *isNull set to false else - * if none is true and at least one clause evaluated - * to NULL we set *isNull flag to true - - * ---------------- - */ - foreach (clause, clauses) { - /* ---------------- - * We don't iterate over sets in the quals, so pass in an isDone - * flag, but ignore it. + * We don't iterate over sets in the quals, so pass in an isDone + * flag, but ignore it. * ---------------- */ - const_value = ExecEvalExpr((Node *) lfirst(clause), - econtext, - isNull, - &isDone); - + expr_value = ExecEvalExpr(clause, econtext, isNull, &isDone); + /* ---------------- - * if the expression evaluates to null, then we - * remember it in the local IsNull flag, if none of the - * clauses are true then we need to set *isNull - * to true again. + * if the expression evaluates to null, then we just + * cascade the null back to whoever called us. * ---------------- */ if (*isNull) - IsNull = *isNull; - + return expr_value; + /* ---------------- - * if we have a true result, then we return it. + * evaluation of 'not' is simple.. expr is false, then + * return 'true' and vice versa. * ---------------- */ - if (DatumGetInt32(const_value) != 0) - return const_value; - } - - /* IsNull is true if at least one clause evaluated to NULL */ - *isNull = IsNull; - return const_value; + if (DatumGetInt32(expr_value) == 0) + return (Datum) true; + + return (Datum) false; } /* ---------------------------------------------------------------- - * ExecEvalAnd + * ExecEvalOr * ---------------------------------------------------------------- */ -static Datum -ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull) +static Datum +ExecEvalOr(Expr * orExpr, ExprContext * econtext, bool * isNull) { - List *clauses; - List *clause; - Datum const_value = 0; - bool isDone; - bool IsNull; - - IsNull = false; - - clauses = andExpr->args; - - /* ---------------- - * we evaluate each of the clauses in turn, - * as soon as one is false we return that - * value. If none are false or NULL then we return - * the value of the last clause evaluated, which - * should be true. - * ---------------- - */ - foreach (clause, clauses) { - - /* ---------------- - * We don't iterate over sets in the quals, so pass in an isDone - * flag, but ignore it. - * ---------------- - */ - const_value = ExecEvalExpr((Node *) lfirst(clause), - econtext, - isNull, - &isDone); - + List *clauses; + List *clause; + bool isDone; + bool IsNull; + Datum const_value = 0; + + IsNull = false; + clauses = orExpr->args; + /* ---------------- - * if the expression evaluates to null, then we - * remember it in IsNull, if none of the clauses after - * this evaluates to false we will have to set *isNull - * to true again. + * we use three valued logic functions here... + * we evaluate each of the clauses in turn, + * as soon as one is true we return that + * value. If none is true and none of the + * clauses evaluate to NULL we return + * the value of the last clause evaluated (which + * should be false) with *isNull set to false else + * if none is true and at least one clause evaluated + * to NULL we set *isNull flag to true - * ---------------- */ - if (*isNull) - IsNull = *isNull; - + foreach(clause, clauses) + { + + /* ---------------- + * We don't iterate over sets in the quals, so pass in an isDone + * flag, but ignore it. + * ---------------- + */ + const_value = ExecEvalExpr((Node *) lfirst(clause), + econtext, + isNull, + &isDone); + + /* ---------------- + * if the expression evaluates to null, then we + * remember it in the local IsNull flag, if none of the + * clauses are true then we need to set *isNull + * to true again. + * ---------------- + */ + if (*isNull) + IsNull = *isNull; + + /* ---------------- + * if we have a true result, then we return it. + * ---------------- + */ + if (DatumGetInt32(const_value) != 0) + return const_value; + } + + /* IsNull is true if at least one clause evaluated to NULL */ + *isNull = IsNull; + return const_value; +} + +/* ---------------------------------------------------------------- + * ExecEvalAnd + * ---------------------------------------------------------------- + */ +static Datum +ExecEvalAnd(Expr * andExpr, ExprContext * econtext, bool * isNull) +{ + List *clauses; + List *clause; + Datum const_value = 0; + bool isDone; + bool IsNull; + + IsNull = false; + + clauses = andExpr->args; + /* ---------------- - * if we have a false result, then we return it, since the - * conjunction must be false. + * we evaluate each of the clauses in turn, + * as soon as one is false we return that + * value. If none are false or NULL then we return + * the value of the last clause evaluated, which + * should be true. * ---------------- */ - if (DatumGetInt32(const_value) == 0) - return const_value; - } - - *isNull = IsNull; - return const_value; + foreach(clause, clauses) + { + + /* ---------------- + * We don't iterate over sets in the quals, so pass in an isDone + * flag, but ignore it. + * ---------------- + */ + const_value = ExecEvalExpr((Node *) lfirst(clause), + econtext, + isNull, + &isDone); + + /* ---------------- + * if the expression evaluates to null, then we + * remember it in IsNull, if none of the clauses after + * this evaluates to false we will have to set *isNull + * to true again. + * ---------------- + */ + if (*isNull) + IsNull = *isNull; + + /* ---------------- + * if we have a false result, then we return it, since the + * conjunction must be false. + * ---------------- + */ + if (DatumGetInt32(const_value) == 0) + return const_value; + } + + *isNull = IsNull; + return const_value; } -/* ---------------------------------------------------------------- - * ExecEvalExpr - * - * Recursively evaluate a targetlist or qualification expression. +/* ---------------------------------------------------------------- + * ExecEvalExpr + * + * Recursively evaluate a targetlist or qualification expression. * - * This routine is an inner loop routine and should be as fast - * as possible. + * This routine is an inner loop routine and should be as fast + * as possible. * - * Node comparison functions were replaced by macros for speed and to plug - * memory leaks incurred by using the planner's Lispy stuff for - * comparisons. Order of evaluation of node comparisons IS IMPORTANT; - * the macros do no checks. Order of evaluation: - * - * o an isnull check, largely to avoid coredumps since greg doubts this - * routine is called with a null ptr anyway in proper operation, but is - * not completely sure... - * o ExactNodeType checks. - * o clause checks or other checks where we look at the lfirst of something. + * Node comparison functions were replaced by macros for speed and to plug + * memory leaks incurred by using the planner's Lispy stuff for + * comparisons. Order of evaluation of node comparisons IS IMPORTANT; + * the macros do no checks. Order of evaluation: + * + * o an isnull check, largely to avoid coredumps since greg doubts this + * routine is called with a null ptr anyway in proper operation, but is + * not completely sure... + * o ExactNodeType checks. + * o clause checks or other checks where we look at the lfirst of something. * ---------------------------------------------------------------- */ Datum -ExecEvalExpr(Node *expression, - ExprContext *econtext, - bool *isNull, - bool *isDone) +ExecEvalExpr(Node * expression, + ExprContext * econtext, + bool * isNull, + bool * isDone) { - Datum retDatum = 0; - - *isNull = false; - - /* - * Some callers don't care about is done and only want 1 result. They - * indicate this by passing NULL - */ - if (isDone) - *isDone = true; - - /* ---------------- - * here we dispatch the work to the appropriate type - * of function given the type of our expression. - * ---------------- - */ - if (expression == NULL) { - *isNull = true; - return (Datum) true; - } - - switch(nodeTag(expression)) { - case T_Var: - retDatum = (Datum) ExecEvalVar((Var *) expression, econtext, isNull); - break; - case T_Const: { - Const *con = (Const *)expression; - - if (con->constisnull) - *isNull = true; - retDatum = con->constvalue; - break; - } - case T_Param: - retDatum = (Datum)ExecEvalParam((Param *)expression, econtext, isNull); - break; - case T_Iter: - retDatum = (Datum) ExecEvalIter((Iter *) expression, - econtext, - isNull, - isDone); - break; - case T_Aggreg: - retDatum = (Datum) ExecEvalAggreg((Aggreg *)expression, - econtext, - isNull); - break; - case T_ArrayRef: - retDatum = (Datum) ExecEvalArrayRef((ArrayRef *) expression, - econtext, - isNull, - isDone); - break; - case T_Expr: { - Expr *expr = (Expr *)expression; - switch (expr->opType) { - case OP_EXPR: - retDatum = (Datum) ExecEvalOper(expr, econtext, isNull); - break; - case FUNC_EXPR: - retDatum = (Datum) ExecEvalFunc(expr, econtext, isNull, isDone); - break; - case OR_EXPR: - retDatum = (Datum) ExecEvalOr(expr, econtext, isNull); - break; - case AND_EXPR: - retDatum = (Datum) ExecEvalAnd(expr, econtext, isNull); - break; - case NOT_EXPR: - retDatum = (Datum) ExecEvalNot(expr, econtext, isNull); - break; + Datum retDatum = 0; + + *isNull = false; + + /* + * Some callers don't care about is done and only want 1 result. They + * indicate this by passing NULL + */ + if (isDone) + *isDone = true; + + /* ---------------- + * here we dispatch the work to the appropriate type + * of function given the type of our expression. + * ---------------- + */ + if (expression == NULL) + { + *isNull = true; + return (Datum) true; + } + + switch (nodeTag(expression)) + { + case T_Var: + retDatum = (Datum) ExecEvalVar((Var *) expression, econtext, isNull); + break; + case T_Const: + { + Const *con = (Const *) expression; + + if (con->constisnull) + *isNull = true; + retDatum = con->constvalue; + break; + } + case T_Param: + retDatum = (Datum) ExecEvalParam((Param *) expression, econtext, isNull); + break; + case T_Iter: + retDatum = (Datum) ExecEvalIter((Iter *) expression, + econtext, + isNull, + isDone); + break; + case T_Aggreg: + retDatum = (Datum) ExecEvalAggreg((Aggreg *) expression, + econtext, + isNull); + break; + case T_ArrayRef: + retDatum = (Datum) ExecEvalArrayRef((ArrayRef *) expression, + econtext, + isNull, + isDone); + break; + case T_Expr: + { + Expr *expr = (Expr *) expression; + + switch (expr->opType) + { + case OP_EXPR: + retDatum = (Datum) ExecEvalOper(expr, econtext, isNull); + break; + case FUNC_EXPR: + retDatum = (Datum) ExecEvalFunc(expr, econtext, isNull, isDone); + break; + case OR_EXPR: + retDatum = (Datum) ExecEvalOr(expr, econtext, isNull); + break; + case AND_EXPR: + retDatum = (Datum) ExecEvalAnd(expr, econtext, isNull); + break; + case NOT_EXPR: + retDatum = (Datum) ExecEvalNot(expr, econtext, isNull); + break; + default: + elog(WARN, "ExecEvalExpr: unknown expression type"); + break; + } + break; + } default: - elog(WARN, "ExecEvalExpr: unknown expression type"); - break; + elog(WARN, "ExecEvalExpr: unknown expression type"); + break; } - break; - } - default: - elog(WARN, "ExecEvalExpr: unknown expression type"); - break; - } - - return retDatum; + + return retDatum; } /* ---------------------------------------------------------------- - * ExecQual / ExecTargetList + * ExecQual / ExecTargetList * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- - * ExecQualClause + * ExecQualClause * - * this is a workhorse for ExecQual. ExecQual has to deal - * with a list of qualifications, so it passes each qualification - * in the list to this function one at a time. ExecQualClause - * returns true when the qualification *fails* and false if - * the qualification succeeded (meaning we have to test the - * rest of the qualification) + * this is a workhorse for ExecQual. ExecQual has to deal + * with a list of qualifications, so it passes each qualification + * in the list to this function one at a time. ExecQualClause + * returns true when the qualification *fails* and false if + * the qualification succeeded (meaning we have to test the + * rest of the qualification) * ---------------------------------------------------------------- */ -static bool -ExecQualClause(Node *clause, ExprContext *econtext) +static bool +ExecQualClause(Node * clause, ExprContext * econtext) { - Datum expr_value; - bool isNull; - bool isDone; - - /* when there is a null clause, consider the qualification to be true */ - if (clause == NULL) - return true; - - /* - * pass isDone, but ignore it. We don't iterate over multiple - * returns in the qualifications. - */ - expr_value = (Datum) - ExecEvalExpr(clause, econtext, &isNull, &isDone); - - /* ---------------- - * this is interesting behaviour here. When a clause evaluates - * to null, then we consider this as passing the qualification. - * it seems kind of like, if the qual is NULL, then there's no - * qual.. - * ---------------- - */ - if (isNull) - return true; - - /* ---------------- - * remember, we return true when the qualification fails.. - * ---------------- - */ - if (DatumGetInt32(expr_value) == 0) - return true; - - return false; + Datum expr_value; + bool isNull; + bool isDone; + + /* when there is a null clause, consider the qualification to be true */ + if (clause == NULL) + return true; + + /* + * pass isDone, but ignore it. We don't iterate over multiple returns + * in the qualifications. + */ + expr_value = (Datum) + ExecEvalExpr(clause, econtext, &isNull, &isDone); + + /* ---------------- + * this is interesting behaviour here. When a clause evaluates + * to null, then we consider this as passing the qualification. + * it seems kind of like, if the qual is NULL, then there's no + * qual.. + * ---------------- + */ + if (isNull) + return true; + + /* ---------------- + * remember, we return true when the qualification fails.. + * ---------------- + */ + if (DatumGetInt32(expr_value) == 0) + return true; + + return false; } /* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression and returns t - * iff none of the subexpressions are false (or null). + * ExecQual + * + * Evaluates a conjunctive boolean expression and returns t + * iff none of the subexpressions are false (or null). * ---------------------------------------------------------------- */ bool -ExecQual(List *qual, ExprContext *econtext) +ExecQual(List * qual, ExprContext * econtext) { - List *clause; - bool result; - - /* ---------------- - * debugging stuff - * ---------------- - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - IncrProcessed(); - - /* ---------------- - * return true immediately if no qual - * ---------------- - */ - if (qual == NIL) - return true; - - /* ---------------- - * a "qual" is a list of clauses. To evaluate the - * qual, we evaluate each of the clauses in the list. - * - * ExecQualClause returns true when we know the qualification - * *failed* so we just pass each clause in qual to it until - * we know the qual failed or there are no more clauses. - * ---------------- - */ - result = false; - foreach (clause, qual) { - result = ExecQualClause((Node *)lfirst(clause), econtext); + List *clause; + bool result; + + /* ---------------- + * debugging stuff + * ---------------- + */ + EV_printf("ExecQual: qual is "); + EV_nodeDisplay(qual); + EV_printf("\n"); + + IncrProcessed(); + + /* ---------------- + * return true immediately if no qual + * ---------------- + */ + if (qual == NIL) + return true; + + /* ---------------- + * a "qual" is a list of clauses. To evaluate the + * qual, we evaluate each of the clauses in the list. + * + * ExecQualClause returns true when we know the qualification + * *failed* so we just pass each clause in qual to it until + * we know the qual failed or there are no more clauses. + * ---------------- + */ + result = false; + foreach(clause, qual) + { + result = ExecQualClause((Node *) lfirst(clause), econtext); + if (result == true) + break; + } + + /* ---------------- + * if result is true, then it means a clause failed so we + * return false. if result is false then it means no clause + * failed so we return true. + * ---------------- + */ if (result == true) - break; - } - - /* ---------------- - * if result is true, then it means a clause failed so we - * return false. if result is false then it means no clause - * failed so we return true. - * ---------------- - */ - if (result == true) - return false; - - return true; + return false; + + return true; } int -ExecTargetListLength(List *targetlist) +ExecTargetListLength(List * targetlist) { - int len; - List *tl; - TargetEntry *curTle; - - len = 0; - foreach (tl, targetlist) { - curTle = lfirst(tl); - - if (curTle->resdom != NULL) - len++; - else - len += curTle->fjoin->fj_nNodes; - } - return len; + int len; + List *tl; + TargetEntry *curTle; + + len = 0; + foreach(tl, targetlist) + { + curTle = lfirst(tl); + + if (curTle->resdom != NULL) + len++; + else + len += curTle->fjoin->fj_nNodes; + } + return len; } /* ---------------------------------------------------------------- - * ExecTargetList - * - * Evaluates a targetlist with respect to the current - * expression context and return a tuple. + * ExecTargetList + * + * Evaluates a targetlist with respect to the current + * expression context and return a tuple. * ---------------------------------------------------------------- */ -static HeapTuple -ExecTargetList(List *targetlist, - int nodomains, - TupleDesc targettype, - Datum *values, - ExprContext *econtext, - bool *isDone) +static HeapTuple +ExecTargetList(List * targetlist, + int nodomains, + TupleDesc targettype, + Datum * values, + ExprContext * econtext, + bool * isDone) { - char nulls_array[64]; - bool fjNullArray[64]; - bool *fjIsNull; - char *null_head; - List *tl; - TargetEntry *tle; - Node *expr; - Resdom *resdom; - AttrNumber resind; - Datum constvalue; - HeapTuple newTuple; - bool isNull; - - /* ---------------- - * debugging stuff - * ---------------- - */ - EV_printf("ExecTargetList: tl is "); - EV_nodeDisplay(targetlist); - EV_printf("\n"); - - /* ---------------- - * Return a dummy tuple if the targetlist is empty . - * the dummy tuple is necessary to differentiate - * between passing and failing the qualification. - * ---------------- - */ - if (targetlist == NIL) { + char nulls_array[64]; + bool fjNullArray[64]; + bool *fjIsNull; + char *null_head; + List *tl; + TargetEntry *tle; + Node *expr; + Resdom *resdom; + AttrNumber resind; + Datum constvalue; + HeapTuple newTuple; + bool isNull; + /* ---------------- - * I now think that the only time this makes - * any sence is when we run a delete query. Then - * we need to return something other than nil - * so we know to delete the tuple associated - * with the saved tupleid.. see what ExecutePlan - * does with the returned tuple.. -cim 9/21/89 - * - * It could also happen in queries like: - * retrieve (foo.all) where bar.a = 3 - * - * is this a new phenomenon? it might cause bogus behavior - * if we try to free this tuple later!! I put a hook in - * ExecProject to watch out for this case -mer 24 Aug 1992 + * debugging stuff * ---------------- */ - CXT1_printf("ExecTargetList: context is %d\n", CurrentMemoryContext); - *isDone = true; - return (HeapTuple) true; - } - - /* ---------------- - * allocate an array of char's to hold the "null" information - * only if we have a really large targetlist. otherwise we use - * the stack. - * ---------------- - */ - if (nodomains > 64) { - null_head = (char *) palloc(nodomains+1); - fjIsNull = (bool *) palloc(nodomains+1); - } else { - null_head = &nulls_array[0]; - fjIsNull = &fjNullArray[0]; - } - - /* ---------------- - * evaluate all the expressions in the target list - * ---------------- - */ - EV_printf("ExecTargetList: setting target list values\n"); - - *isDone = true; - foreach (tl, targetlist) { + EV_printf("ExecTargetList: tl is "); + EV_nodeDisplay(targetlist); + EV_printf("\n"); + /* ---------------- - * remember, a target list is a list of lists: - * - * ((<resdom | fjoin> expr) (<resdom | fjoin> expr) ...) - * - * tl is a pointer to successive cdr's of the targetlist - * tle is a pointer to the target list entry in tl + * Return a dummy tuple if the targetlist is empty . + * the dummy tuple is necessary to differentiate + * between passing and failing the qualification. + * ---------------- + */ + if (targetlist == NIL) + { + /* ---------------- + * I now think that the only time this makes + * any sence is when we run a delete query. Then + * we need to return something other than nil + * so we know to delete the tuple associated + * with the saved tupleid.. see what ExecutePlan + * does with the returned tuple.. -cim 9/21/89 + * + * It could also happen in queries like: + * retrieve (foo.all) where bar.a = 3 + * + * is this a new phenomenon? it might cause bogus behavior + * if we try to free this tuple later!! I put a hook in + * ExecProject to watch out for this case -mer 24 Aug 1992 + * ---------------- + */ + CXT1_printf("ExecTargetList: context is %d\n", CurrentMemoryContext); + *isDone = true; + return (HeapTuple) true; + } + + /* ---------------- + * allocate an array of char's to hold the "null" information + * only if we have a really large targetlist. otherwise we use + * the stack. * ---------------- */ - tle = lfirst(tl); - - if (tle->resdom != NULL) { - expr = tle->expr; - resdom = tle->resdom; - resind = resdom->resno - 1; - constvalue = (Datum) ExecEvalExpr(expr, - econtext, - &isNull, - isDone); - - if ((IsA(expr,Iter)) && (*isDone)) - return (HeapTuple)NULL; - - values[resind] = constvalue; - - if (!isNull) - null_head[resind] = ' '; - else - null_head[resind] = 'n'; - }else { - int curNode; - Resdom *fjRes; - List *fjTlist = (List *)tle->expr; - Fjoin *fjNode = tle->fjoin; - int nNodes = fjNode->fj_nNodes; - DatumPtr results = fjNode->fj_results; - - ExecEvalFjoin(tle, econtext, fjIsNull, isDone); - if (*isDone) - return (HeapTuple)NULL; - - /* - * get the result from the inner node - */ - fjRes = (Resdom *)fjNode->fj_innerNode; - resind = fjRes->resno - 1; - if (fjIsNull[0]) - null_head[resind] = 'n'; - else { - null_head[resind] = ' '; - values[resind] = results[0]; - } - - /* - * Get results from all of the outer nodes - */ - for (curNode = 1; - curNode < nNodes; - curNode++, fjTlist = lnext(fjTlist)) + if (nodomains > 64) + { + null_head = (char *) palloc(nodomains + 1); + fjIsNull = (bool *) palloc(nodomains + 1); + } + else + { + null_head = &nulls_array[0]; + fjIsNull = &fjNullArray[0]; + } + + /* ---------------- + * evaluate all the expressions in the target list + * ---------------- + */ + EV_printf("ExecTargetList: setting target list values\n"); + + *isDone = true; + foreach(tl, targetlist) + { + /* ---------------- + * remember, a target list is a list of lists: + * + * ((<resdom | fjoin> expr) (<resdom | fjoin> expr) ...) + * + * tl is a pointer to successive cdr's of the targetlist + * tle is a pointer to the target list entry in tl + * ---------------- + */ + tle = lfirst(tl); + + if (tle->resdom != NULL) + { + expr = tle->expr; + resdom = tle->resdom; + resind = resdom->resno - 1; + constvalue = (Datum) ExecEvalExpr(expr, + econtext, + &isNull, + isDone); + + if ((IsA(expr, Iter)) && (*isDone)) + return (HeapTuple) NULL; + + values[resind] = constvalue; + + if (!isNull) + null_head[resind] = ' '; + else + null_head[resind] = 'n'; + } + else { -#if 0 /* what is this?? */ - Node *outernode = lfirst(fjTlist); - fjRes = (Resdom *)outernode->iterexpr; -#endif - resind = fjRes->resno - 1; - if (fjIsNull[curNode]) { - null_head[resind] = 'n'; - }else { - null_head[resind] = ' '; - values[resind] = results[curNode]; - } + int curNode; + Resdom *fjRes; + List *fjTlist = (List *) tle->expr; + Fjoin *fjNode = tle->fjoin; + int nNodes = fjNode->fj_nNodes; + DatumPtr results = fjNode->fj_results; + + ExecEvalFjoin(tle, econtext, fjIsNull, isDone); + if (*isDone) + return (HeapTuple) NULL; + + /* + * get the result from the inner node + */ + fjRes = (Resdom *) fjNode->fj_innerNode; + resind = fjRes->resno - 1; + if (fjIsNull[0]) + null_head[resind] = 'n'; + else + { + null_head[resind] = ' '; + values[resind] = results[0]; + } + + /* + * Get results from all of the outer nodes + */ + for (curNode = 1; + curNode < nNodes; + curNode++, fjTlist = lnext(fjTlist)) + { +#if 0 /* what is this?? */ + Node *outernode = lfirst(fjTlist); + + fjRes = (Resdom *) outernode->iterexpr; +#endif + resind = fjRes->resno - 1; + if (fjIsNull[curNode]) + { + null_head[resind] = 'n'; + } + else + { + null_head[resind] = ' '; + values[resind] = results[curNode]; + } + } } } - } - - /* ---------------- - * form the new result tuple (in the "normal" context) - * ---------------- - */ - newTuple = (HeapTuple) - heap_formtuple(targettype, values, null_head); - - /* ---------------- - * free the nulls array if we allocated one.. - * ---------------- - */ - if (nodomains > 64) pfree(null_head); - - return - newTuple; + + /* ---------------- + * form the new result tuple (in the "normal" context) + * ---------------- + */ + newTuple = (HeapTuple) + heap_formtuple(targettype, values, null_head); + + /* ---------------- + * free the nulls array if we allocated one.. + * ---------------- + */ + if (nodomains > 64) + pfree(null_head); + + return + newTuple; } /* ---------------------------------------------------------------- - * ExecProject - * - * projects a tuple based in projection info and stores - * it in the specified tuple table slot. + * ExecProject + * + * projects a tuple based in projection info and stores + * it in the specified tuple table slot. * - * Note: someday soon the executor can be extended to eliminate - * redundant projections by storing pointers to datums - * in the tuple table and then passing these around when - * possible. this should make things much quicker. - * -cim 6/3/91 + * Note: someday soon the executor can be extended to eliminate + * redundant projections by storing pointers to datums + * in the tuple table and then passing these around when + * possible. this should make things much quicker. + * -cim 6/3/91 * ---------------------------------------------------------------- */ TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, bool *isDone) +ExecProject(ProjectionInfo * projInfo, bool * isDone) { - TupleTableSlot *slot; - List *targetlist; - int len; - TupleDesc tupType; - Datum *tupValue; - ExprContext *econtext; - HeapTuple newTuple; - - /* ---------------- - * sanity checks - * ---------------- - */ - if (projInfo == NULL) - return (TupleTableSlot *) NULL; - - /* ---------------- - * get the projection info we want - * ---------------- - */ - slot = projInfo->pi_slot; - targetlist = projInfo->pi_targetlist; - len = projInfo->pi_len; - tupType = slot->ttc_tupleDescriptor; - - tupValue = projInfo->pi_tupValue; - econtext = projInfo->pi_exprContext; - - if (targetlist == NIL) { - *isDone = true; - return (TupleTableSlot *) NULL; - } - - /* ---------------- - * form a new (result) tuple - * ---------------- - */ - newTuple = ExecTargetList(targetlist, - len, - tupType, - tupValue, - econtext, - isDone); - - /* ---------------- - * store the tuple in the projection slot and return the slot. - * - * If there's no projection target list we don't want to pfree - * the bogus tuple that ExecTargetList passes back to us. - * -mer 24 Aug 1992 - * ---------------- - */ - return (TupleTableSlot *) - ExecStoreTuple(newTuple, /* tuple to store */ - slot, /* slot to store in */ - InvalidBuffer, /* tuple has no buffer */ - true); -} + TupleTableSlot *slot; + List *targetlist; + int len; + TupleDesc tupType; + Datum *tupValue; + ExprContext *econtext; + HeapTuple newTuple; + + /* ---------------- + * sanity checks + * ---------------- + */ + if (projInfo == NULL) + return (TupleTableSlot *) NULL; + /* ---------------- + * get the projection info we want + * ---------------- + */ + slot = projInfo->pi_slot; + targetlist = projInfo->pi_targetlist; + len = projInfo->pi_len; + tupType = slot->ttc_tupleDescriptor; + + tupValue = projInfo->pi_tupValue; + econtext = projInfo->pi_exprContext; + + if (targetlist == NIL) + { + *isDone = true; + return (TupleTableSlot *) NULL; + } + + /* ---------------- + * form a new (result) tuple + * ---------------- + */ + newTuple = ExecTargetList(targetlist, + len, + tupType, + tupValue, + econtext, + isDone); + + /* ---------------- + * store the tuple in the projection slot and return the slot. + * + * If there's no projection target list we don't want to pfree + * the bogus tuple that ExecTargetList passes back to us. + * -mer 24 Aug 1992 + * ---------------- + */ + return (TupleTableSlot *) + ExecStoreTuple(newTuple,/* tuple to store */ + slot, /* slot to store in */ + InvalidBuffer, /* tuple has no buffer */ + true); +} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index 8e69f491731..6ea50bb2a93 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -1,17 +1,17 @@ /*------------------------------------------------------------------------- * * execScan.c-- - * This code provides support for generalized relation scans. ExecScan - * is passed a node and a pointer to a function to "do the right thing" - * and return a tuple from the relation. ExecScan then does the tedious - * stuff - checking the qualification and projecting the tuple - * appropriately. + * This code provides support for generalized relation scans. ExecScan + * is passed a node and a pointer to a function to "do the right thing" + * and return a tuple from the relation. ExecScan then does the tedious + * stuff - checking the qualification and projecting the tuple + * appropriately. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.3 1997/07/28 00:53:51 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.4 1997/09/07 04:41:23 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -23,117 +23,123 @@ #include "executor/executor.h" /* ---------------------------------------------------------------- - * ExecScan - * - * Scans the relation using the 'access method' indicated and - * returns the next qualifying tuple in the direction specified - * in the global variable ExecDirection. - * The access method returns the next tuple and execScan() is - * responisble for checking the tuple returned against the qual-clause. - * - * Conditions: - * -- the "cursor" maintained by the AMI is positioned at the tuple - * returned previously. - * - * Initial States: - * -- the relation indicated is opened for scanning so that the - * "cursor" is positioned before the first qualifying tuple. + * ExecScan * - * May need to put startmmgr and endmmgr in here. + * Scans the relation using the 'access method' indicated and + * returns the next qualifying tuple in the direction specified + * in the global variable ExecDirection. + * The access method returns the next tuple and execScan() is + * responisble for checking the tuple returned against the qual-clause. + * + * Conditions: + * -- the "cursor" maintained by the AMI is positioned at the tuple + * returned previously. + * + * Initial States: + * -- the relation indicated is opened for scanning so that the + * "cursor" is positioned before the first qualifying tuple. + * + * May need to put startmmgr and endmmgr in here. * ---------------------------------------------------------------- */ TupleTableSlot * -ExecScan(Scan *node, - TupleTableSlot* (*accessMtd)()) /* function returning a tuple */ +ExecScan(Scan * node, + TupleTableSlot * (*accessMtd) ()) /* function returning a + * tuple */ { - CommonScanState *scanstate; - EState *estate; - List *qual; - bool isDone; - - TupleTableSlot *slot; - TupleTableSlot *resultSlot; - HeapTuple newTuple; - - ExprContext *econtext; - ProjectionInfo *projInfo; - - - /* ---------------- - * initialize misc variables - * ---------------- - */ - newTuple = NULL; - slot = NULL; - - estate = node->plan.state; - scanstate = node->scanstate; - - /* ---------------- - * get the expression context - * ---------------- - */ - econtext = scanstate->cstate.cs_ExprContext; - - /* ---------------- - * initialize fields in ExprContext which don't change - * in the course of the scan.. - * ---------------- - */ - qual = node->plan.qual; - econtext->ecxt_relation = scanstate->css_currentRelation; - econtext->ecxt_relid = node->scanrelid; - - if (scanstate->cstate.cs_TupFromTlist) { - projInfo = scanstate->cstate.cs_ProjInfo; - resultSlot = ExecProject(projInfo, &isDone); - if (!isDone) - return resultSlot; - } - /* - * get a tuple from the access method - * loop until we obtain a tuple which passes the qualification. - */ - for(;;) { - slot = (TupleTableSlot *) (*accessMtd)(node); + CommonScanState *scanstate; + EState *estate; + List *qual; + bool isDone; + + TupleTableSlot *slot; + TupleTableSlot *resultSlot; + HeapTuple newTuple; + + ExprContext *econtext; + ProjectionInfo *projInfo; + /* ---------------- - * if the slot returned by the accessMtd contains - * NULL, then it means there is nothing more to scan - * so we just return the empty slot. + * initialize misc variables * ---------------- */ - if (TupIsNull(slot)) return slot; - + newTuple = NULL; + slot = NULL; + + estate = node->plan.state; + scanstate = node->scanstate; + /* ---------------- - * place the current tuple into the expr context + * get the expression context * ---------------- */ - econtext->ecxt_scantuple = slot; - + econtext = scanstate->cstate.cs_ExprContext; + /* ---------------- - * check that the current tuple satisfies the qual-clause - * if our qualification succeeds then we - * leave the loop. + * initialize fields in ExprContext which don't change + * in the course of the scan.. * ---------------- */ + qual = node->plan.qual; + econtext->ecxt_relation = scanstate->css_currentRelation; + econtext->ecxt_relid = node->scanrelid; + + if (scanstate->cstate.cs_TupFromTlist) + { + projInfo = scanstate->cstate.cs_ProjInfo; + resultSlot = ExecProject(projInfo, &isDone); + if (!isDone) + return resultSlot; + } + + /* + * get a tuple from the access method loop until we obtain a tuple + * which passes the qualification. + */ + for (;;) + { + slot = (TupleTableSlot *) (*accessMtd) (node); + + /* ---------------- + * if the slot returned by the accessMtd contains + * NULL, then it means there is nothing more to scan + * so we just return the empty slot. + * ---------------- + */ + if (TupIsNull(slot)) + return slot; + + /* ---------------- + * place the current tuple into the expr context + * ---------------- + */ + econtext->ecxt_scantuple = slot; + + /* ---------------- + * check that the current tuple satisfies the qual-clause + * if our qualification succeeds then we + * leave the loop. + * ---------------- + */ + + /* + * add a check for non-nil qual here to avoid a function call to + * ExecQual() when the qual is nil + */ + if (!qual || ExecQual(qual, econtext) == true) + break; + } - /* add a check for non-nil qual here to avoid a - function call to ExecQual() when the qual is nil */ - if (!qual || ExecQual(qual, econtext) == true) - break; - } - - /* ---------------- - * form a projection tuple, store it in the result tuple - * slot and return it. - * ---------------- - */ - projInfo = scanstate->cstate.cs_ProjInfo; + /* ---------------- + * form a projection tuple, store it in the result tuple + * slot and return it. + * ---------------- + */ + projInfo = scanstate->cstate.cs_ProjInfo; - resultSlot = ExecProject(projInfo, &isDone); - scanstate->cstate.cs_TupFromTlist = !isDone; + resultSlot = ExecProject(projInfo, &isDone); + scanstate->cstate.cs_TupFromTlist = !isDone; - return resultSlot; + return resultSlot; } - diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 0d5e7fda9fb..287f75699af 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -1,119 +1,119 @@ /*------------------------------------------------------------------------- * * execTuples.c-- - * Routines dealing with the executor tuple tables. These are used to - * ensure that the executor frees copies of tuples (made by - * ExecTargetList) properly. + * Routines dealing with the executor tuple tables. These are used to + * ensure that the executor frees copies of tuples (made by + * ExecTargetList) properly. + * + * Routines dealing with the type information for tuples. Currently, + * the type information for a tuple is an array of FormData_pg_attribute. + * This information is needed by routines manipulating tuples + * (getattribute, formtuple, etc.). * - * Routines dealing with the type information for tuples. Currently, - * the type information for a tuple is an array of FormData_pg_attribute. - * This information is needed by routines manipulating tuples - * (getattribute, formtuple, etc.). - * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.6 1997/08/19 21:31:05 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.7 1997/09/07 04:41:24 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES * - * TABLE CREATE/DELETE - * ExecCreateTupleTable - create a new tuple table - * ExecDestroyTupleTable - destroy a table + * TABLE CREATE/DELETE + * ExecCreateTupleTable - create a new tuple table + * ExecDestroyTupleTable - destroy a table * - * SLOT RESERVERATION - * ExecAllocTableSlot - find an available slot in the table + * SLOT RESERVERATION + * ExecAllocTableSlot - find an available slot in the table * - * SLOT ACCESSORS - * ExecStoreTuple - store a tuple in the table - * ExecFetchTuple - fetch a tuple from the table - * ExecClearTuple - clear contents of a table slot - * ExecSlotPolicy - return slot's tuple pfree policy - * ExecSetSlotPolicy - diddle the slot policy - * ExecSlotDescriptor - type of tuple in a slot - * ExecSetSlotDescriptor - set a slot's tuple descriptor - * ExecSetSlotDescriptorIsNew - diddle the slot-desc-is-new flag - * ExecSetNewSlotDescriptor - set a desc and the is-new-flag all at once - * ExecSlotBuffer - return buffer of tuple in slot - * ExecSetSlotBuffer - set the buffer for tuple in slot - * ExecIncrSlotBufferRefcnt - bump the refcnt of the slot buffer + * SLOT ACCESSORS + * ExecStoreTuple - store a tuple in the table + * ExecFetchTuple - fetch a tuple from the table + * ExecClearTuple - clear contents of a table slot + * ExecSlotPolicy - return slot's tuple pfree policy + * ExecSetSlotPolicy - diddle the slot policy + * ExecSlotDescriptor - type of tuple in a slot + * ExecSetSlotDescriptor - set a slot's tuple descriptor + * ExecSetSlotDescriptorIsNew - diddle the slot-desc-is-new flag + * ExecSetNewSlotDescriptor - set a desc and the is-new-flag all at once + * ExecSlotBuffer - return buffer of tuple in slot + * ExecSetSlotBuffer - set the buffer for tuple in slot + * ExecIncrSlotBufferRefcnt - bump the refcnt of the slot buffer * - * SLOT STATUS PREDICATES - * TupIsNull - true when slot contains no tuple - * ExecSlotDescriptorIsNew - true if we're now storing a different - * type of tuple in a slot + * SLOT STATUS PREDICATES + * TupIsNull - true when slot contains no tuple + * ExecSlotDescriptorIsNew - true if we're now storing a different + * type of tuple in a slot * - * CONVENIENCE INITIALIZATION ROUTINES - * ExecInitResultTupleSlot \ convience routines to initialize - * ExecInitScanTupleSlot \ the various tuple slots for nodes - * ExecInitMarkedTupleSlot / which store copies of tuples. - * ExecInitOuterTupleSlot / - * ExecInitHashTupleSlot / + * CONVENIENCE INITIALIZATION ROUTINES + * ExecInitResultTupleSlot \ convience routines to initialize + * ExecInitScanTupleSlot \ the various tuple slots for nodes + * ExecInitMarkedTupleSlot / which store copies of tuples. + * ExecInitOuterTupleSlot / + * ExecInitHashTupleSlot / * - * old routines: - * ExecGetTupType - get type of tuple returned by this node - * ExecTypeFromTL - form a TupleDesc from a target list + * old routines: + * ExecGetTupType - get type of tuple returned by this node + * ExecTypeFromTL - form a TupleDesc from a target list * - * EXAMPLE OF HOW TABLE ROUTINES WORK - * Suppose we have a query such as retrieve (EMP.name) and we have - * a single SeqScan node in the query plan. + * EXAMPLE OF HOW TABLE ROUTINES WORK + * Suppose we have a query such as retrieve (EMP.name) and we have + * a single SeqScan node in the query plan. * - * At ExecStart() - * ---------------- - * - InitPlan() calls ExecCreateTupleTable() to create the tuple - * table which will hold tuples processed by the executor. + * At ExecStart() + * ---------------- + * - InitPlan() calls ExecCreateTupleTable() to create the tuple + * table which will hold tuples processed by the executor. * - * - ExecInitSeqScan() calls ExecInitScanTupleSlot() and - * ExecInitResultTupleSlot() to reserve places in the tuple - * table for the tuples returned by the access methods and the - * tuples resulting from preforming target list projections. + * - ExecInitSeqScan() calls ExecInitScanTupleSlot() and + * ExecInitResultTupleSlot() to reserve places in the tuple + * table for the tuples returned by the access methods and the + * tuples resulting from preforming target list projections. * - * During ExecRun() - * ---------------- - * - SeqNext() calls ExecStoreTuple() to place the tuple returned - * by the access methods into the scan tuple slot. + * During ExecRun() + * ---------------- + * - SeqNext() calls ExecStoreTuple() to place the tuple returned + * by the access methods into the scan tuple slot. * - * - ExecSeqScan() calls ExecStoreTuple() to take the result - * tuple from ExecTargetList() and place it into the result tuple - * slot. + * - ExecSeqScan() calls ExecStoreTuple() to take the result + * tuple from ExecTargetList() and place it into the result tuple + * slot. * - * - ExecutePlan() calls ExecRetrieve() which gets the tuple out of - * the slot passed to it by calling ExecFetchTuple(). this tuple - * is then returned. + * - ExecutePlan() calls ExecRetrieve() which gets the tuple out of + * the slot passed to it by calling ExecFetchTuple(). this tuple + * is then returned. * - * At ExecEnd() - * ---------------- - * - EndPlan() calls ExecDestroyTupleTable() to clean up any remaining - * tuples left over from executing the query. + * At ExecEnd() + * ---------------- + * - EndPlan() calls ExecDestroyTupleTable() to clean up any remaining + * tuples left over from executing the query. * - * The important thing to watch in the executor code is how pointers - * to the slots containing tuples are passed instead of the tuples - * themselves. This facilitates the communication of related information - * (such as whether or not a tuple should be pfreed, what buffer contains - * this tuple, the tuple's tuple descriptor, etc). Note that much of - * this information is also kept in the ExprContext of each node. - * Soon the executor will be redesigned and ExprContext's will contain - * only slot pointers. -cim 3/14/91 + * The important thing to watch in the executor code is how pointers + * to the slots containing tuples are passed instead of the tuples + * themselves. This facilitates the communication of related information + * (such as whether or not a tuple should be pfreed, what buffer contains + * this tuple, the tuple's tuple descriptor, etc). Note that much of + * this information is also kept in the ExprContext of each node. + * Soon the executor will be redesigned and ExprContext's will contain + * only slot pointers. -cim 3/14/91 * - * NOTES - * The tuple table stuff is relatively new, put here to alleviate - * the process growth problems in the executor. The other routines - * are old (from the original lisp system) and may someday become - * obsolete. -cim 6/23/90 + * NOTES + * The tuple table stuff is relatively new, put here to alleviate + * the process growth problems in the executor. The other routines + * are old (from the original lisp system) and may someday become + * obsolete. -cim 6/23/90 * - * In the implementation of nested-dot queries such as - * "retrieve (EMP.hobbies.all)", a single scan may return tuples - * of many types, so now we return pointers to tuple descriptors - * along with tuples returned via the tuple table. This means - * we now have a bunch of routines to diddle the slot descriptors - * too. -cim 1/18/90 + * In the implementation of nested-dot queries such as + * "retrieve (EMP.hobbies.all)", a single scan may return tuples + * of many types, so now we return pointers to tuple descriptors + * along with tuples returned via the tuple table. This means + * we now have a bunch of routines to diddle the slot descriptors + * too. -cim 1/18/90 * - * The tuple table stuff depends on the executor/tuptable.h macros, - * and the TupleTableSlot node in execnodes.h. + * The tuple table stuff depends on the executor/tuptable.h macros, + * and the TupleTableSlot node in execnodes.h. * */ #include <string.h> @@ -131,902 +131,938 @@ #include "parser/catalog_utils.h" #include "catalog/pg_type.h" -static TupleTableSlot *NodeGetResultTupleSlot(Plan *node); +static TupleTableSlot *NodeGetResultTupleSlot(Plan * node); /* ---------------------------------------------------------------- - * tuple table create/delete functions + * tuple table create/delete functions * ---------------------------------------------------------------- */ /* -------------------------------- - * ExecCreateTupleTable + * ExecCreateTupleTable * - * This creates a new tuple table of the specified initial - * size. If the size is insufficient, ExecAllocTableSlot() - * will grow the table as necessary. + * This creates a new tuple table of the specified initial + * size. If the size is insufficient, ExecAllocTableSlot() + * will grow the table as necessary. * - * This should be used by InitPlan() to allocate the table. - * The table's address will be stored in the EState structure. + * This should be used by InitPlan() to allocate the table. + * The table's address will be stored in the EState structure. * -------------------------------- */ -TupleTable /* return: address of table */ -ExecCreateTupleTable(int initialSize) /* initial number of slots in table */ +TupleTable /* return: address of table */ +ExecCreateTupleTable(int initialSize) /* initial number of slots + * in table */ { - TupleTable newtable; /* newly allocated table */ - TupleTableSlot* array; /* newly allocated slot array */ - - /* ---------------- - * sanity checks - * ---------------- - */ - Assert(initialSize >= 1); - - /* ---------------- - * Now allocate our new table along with space for the pointers - * to the tuples. - */ - - newtable = (TupleTable) palloc(sizeof(TupleTableData)); - array = (TupleTableSlot*) palloc(initialSize * sizeof(TupleTableSlot)); - - /* ---------------- - * clean out the slots we just allocated - * ---------------- - */ - memset(array, 0, initialSize * sizeof(TupleTableSlot)); - - /* ---------------- - * initialize the new table and return it to the caller. - * ---------------- - */ - newtable->size = initialSize; - newtable->next = 0; - newtable->array = array; - - return newtable; + TupleTable newtable; /* newly allocated table */ + TupleTableSlot *array; /* newly allocated slot array */ + + /* ---------------- + * sanity checks + * ---------------- + */ + Assert(initialSize >= 1); + + /* ---------------- + * Now allocate our new table along with space for the pointers + * to the tuples. + */ + + newtable = (TupleTable) palloc(sizeof(TupleTableData)); + array = (TupleTableSlot *) palloc(initialSize * sizeof(TupleTableSlot)); + + /* ---------------- + * clean out the slots we just allocated + * ---------------- + */ + memset(array, 0, initialSize * sizeof(TupleTableSlot)); + + /* ---------------- + * initialize the new table and return it to the caller. + * ---------------- + */ + newtable->size = initialSize; + newtable->next = 0; + newtable->array = array; + + return newtable; } /* -------------------------------- - * ExecDestroyTupleTable + * ExecDestroyTupleTable * - * This pfrees the storage assigned to the tuple table and - * optionally pfrees the contents of the table also. - * It is expected that this routine be called by EndPlan(). + * This pfrees the storage assigned to the tuple table and + * optionally pfrees the contents of the table also. + * It is expected that this routine be called by EndPlan(). * -------------------------------- */ void -ExecDestroyTupleTable(TupleTable table, /* tuple table */ - bool shouldFree) /* true if we should free slot contents */ +ExecDestroyTupleTable(TupleTable table, /* tuple table */ + bool shouldFree) /* true if we should free slot + * contents */ { - int next; /* next avaliable slot */ - TupleTableSlot *array; /* start of table array */ - int i; /* counter */ - - /* ---------------- - * sanity checks - * ---------------- - */ - Assert(table != NULL); - - /* ---------------- - * get information from the table - * ---------------- - */ - array = table->array; - next = table->next; - - /* ---------------- - * first free all the valid pointers in the tuple array - * if that's what the caller wants.. - * - * Note: we do nothing about the Buffer and Tuple Descriptor's - * we store in the slots. This may have to change (ex: we should - * probably worry about pfreeing tuple descs too) -cim 3/14/91 - * ---------------- - */ - if (shouldFree) - for (i = 0; i < next; i++) { - TupleTableSlot slot; - HeapTuple tuple; - - slot = array[i]; - tuple = slot.val; - - if (tuple != NULL) { - slot.val = (HeapTuple)NULL; - if (slot.ttc_shouldFree) { - /* ---------------- - * since a tuple may contain a pointer to - * lock information allocated along with the - * tuple, we have to be careful to free any - * rule locks also -cim 1/17/90 - * ---------------- - */ - pfree(tuple); + int next; /* next avaliable slot */ + TupleTableSlot *array; /* start of table array */ + int i; /* counter */ + + /* ---------------- + * sanity checks + * ---------------- + */ + Assert(table != NULL); + + /* ---------------- + * get information from the table + * ---------------- + */ + array = table->array; + next = table->next; + + /* ---------------- + * first free all the valid pointers in the tuple array + * if that's what the caller wants.. + * + * Note: we do nothing about the Buffer and Tuple Descriptor's + * we store in the slots. This may have to change (ex: we should + * probably worry about pfreeing tuple descs too) -cim 3/14/91 + * ---------------- + */ + if (shouldFree) + for (i = 0; i < next; i++) + { + TupleTableSlot slot; + HeapTuple tuple; + + slot = array[i]; + tuple = slot.val; + + if (tuple != NULL) + { + slot.val = (HeapTuple) NULL; + if (slot.ttc_shouldFree) + { + /* ---------------- + * since a tuple may contain a pointer to + * lock information allocated along with the + * tuple, we have to be careful to free any + * rule locks also -cim 1/17/90 + * ---------------- + */ + pfree(tuple); + } + } } - } - } - - /* ---------------- - * finally free the tuple array and the table itself. - * ---------------- - */ - pfree(array); - pfree(table); - + + /* ---------------- + * finally free the tuple array and the table itself. + * ---------------- + */ + pfree(array); + pfree(table); + } /* ---------------------------------------------------------------- - * tuple table slot reservation functions + * tuple table slot reservation functions * ---------------------------------------------------------------- */ /* -------------------------------- - * ExecAllocTableSlot + * ExecAllocTableSlot * - * This routine is used to reserve slots in the table for - * use by the various plan nodes. It is expected to be - * called by the node init routines (ex: ExecInitNestLoop). - * once per slot needed by the node. Not all nodes need - * slots (some just pass tuples around). + * This routine is used to reserve slots in the table for + * use by the various plan nodes. It is expected to be + * called by the node init routines (ex: ExecInitNestLoop). + * once per slot needed by the node. Not all nodes need + * slots (some just pass tuples around). * -------------------------------- */ -TupleTableSlot* /* return: the slot allocated in the tuple table */ +TupleTableSlot * /* return: the slot allocated in the tuple + * table */ ExecAllocTableSlot(TupleTable table) { - int slotnum; /* new slot number */ - - /* ---------------- - * sanity checks - * ---------------- - */ - Assert(table != NULL); - - /* ---------------- - * if our table is full we have to allocate a larger - * size table. Since ExecAllocTableSlot() is only called - * before the table is ever used to store tuples, we don't - * have to worry about the contents of the old table. - * If this changes, then we will have to preserve the contents. - * -cim 6/23/90 - * - * Unfortunately, we *cannot* do this. All of the nodes in - * the plan that have already initialized their slots will have - * pointers into _freed_ memory. This leads to bad ends. We - * now count the number of slots we will need and create all the - * slots we will need ahead of time. The if below should never - * happen now. Give a WARN if it does. -mer 4 Aug 1992 - * ---------------- - */ - if (table->next >= table->size) { - /* - * int newsize = NewTableSize(table->size); + int slotnum; /* new slot number */ + + /* ---------------- + * sanity checks + * ---------------- + */ + Assert(table != NULL); + + /* ---------------- + * if our table is full we have to allocate a larger + * size table. Since ExecAllocTableSlot() is only called + * before the table is ever used to store tuples, we don't + * have to worry about the contents of the old table. + * If this changes, then we will have to preserve the contents. + * -cim 6/23/90 * - * pfree(table->array); - * table->array = (Pointer) palloc(newsize * TableSlotSize); - * bzero(table->array, newsize * TableSlotSize); - * table->size = newsize; + * Unfortunately, we *cannot* do this. All of the nodes in + * the plan that have already initialized their slots will have + * pointers into _freed_ memory. This leads to bad ends. We + * now count the number of slots we will need and create all the + * slots we will need ahead of time. The if below should never + * happen now. Give a WARN if it does. -mer 4 Aug 1992 + * ---------------- */ - elog(NOTICE, "Plan requires more slots than are available"); - elog(WARN, "send mail to your local executor guru to fix this"); - } - - /* ---------------- - * at this point, space in the table is guaranteed so we - * reserve the next slot, initialize and return it. - * ---------------- - */ - slotnum = table->next; - table->next++; - - table->array[slotnum].type = T_TupleTableSlot; - - return &(table->array[slotnum]); + if (table->next >= table->size) + { + + /* + * int newsize = NewTableSize(table->size); + * + * pfree(table->array); table->array = (Pointer) palloc(newsize * + * TableSlotSize); bzero(table->array, newsize * TableSlotSize); + * table->size = newsize; + */ + elog(NOTICE, "Plan requires more slots than are available"); + elog(WARN, "send mail to your local executor guru to fix this"); + } + + /* ---------------- + * at this point, space in the table is guaranteed so we + * reserve the next slot, initialize and return it. + * ---------------- + */ + slotnum = table->next; + table->next++; + + table->array[slotnum].type = T_TupleTableSlot; + + return &(table->array[slotnum]); } /* ---------------------------------------------------------------- - * tuple table slot accessor functions + * tuple table slot accessor functions * ---------------------------------------------------------------- */ /* -------------------------------- - * ExecStoreTuple + * ExecStoreTuple * - * This function is used to store a tuple into a specified - * slot in the tuple table. Note: the only slots which should - * be called with shouldFree == false are those slots used to - * store tuples not allocated with pfree(). Currently the - * seqscan and indexscan nodes use this for the tuples returned - * by amgetattr, which are actually pointers onto disk pages. + * This function is used to store a tuple into a specified + * slot in the tuple table. Note: the only slots which should + * be called with shouldFree == false are those slots used to + * store tuples not allocated with pfree(). Currently the + * seqscan and indexscan nodes use this for the tuples returned + * by amgetattr, which are actually pointers onto disk pages. * -------------------------------- */ -TupleTableSlot* /* return: slot passed */ -ExecStoreTuple(HeapTuple tuple, /* tuple to store */ - TupleTableSlot* slot, /* slot in which to store tuple */ - Buffer buffer, /* buffer associated with tuple */ - bool shouldFree) /* true if we call pfree() when we gc. */ +TupleTableSlot * /* return: slot passed */ +ExecStoreTuple(HeapTuple tuple, /* tuple to store */ + TupleTableSlot * slot, /* slot in which to store tuple */ + Buffer buffer, /* buffer associated with tuple */ + bool shouldFree) /* true if we call pfree() when we gc. */ { - /* ---------------- - * sanity checks - * ---------------- - */ - Assert(slot != NULL); - - /* clear out the slot first */ - ExecClearTuple(slot); - - /* ---------------- - * store the new tuple into the specified slot and - * return the slot into which we stored the tuple. - * ---------------- - */ - slot->val = tuple; - slot->ttc_buffer = buffer; - slot->ttc_shouldFree = shouldFree; - - return slot; + /* ---------------- + * sanity checks + * ---------------- + */ + Assert(slot != NULL); + + /* clear out the slot first */ + ExecClearTuple(slot); + + /* ---------------- + * store the new tuple into the specified slot and + * return the slot into which we stored the tuple. + * ---------------- + */ + slot->val = tuple; + slot->ttc_buffer = buffer; + slot->ttc_shouldFree = shouldFree; + + return slot; } /* -------------------------------- - * ExecClearTuple + * ExecClearTuple * - * This function is used to clear out a slot in the tuple table. + * This function is used to clear out a slot in the tuple table. * -------------------------------- */ -TupleTableSlot* /* return: slot passed */ -ExecClearTuple(TupleTableSlot* slot) /* slot in which to store tuple */ +TupleTableSlot * /* return: slot passed */ +ExecClearTuple(TupleTableSlot * slot) /* slot in which to store tuple */ { - HeapTuple oldtuple; /* prior contents of slot */ - - /* ---------------- - * sanity checks - * ---------------- - */ - Assert(slot != NULL); - - /* ---------------- - * get information from the tuple table - * ---------------- - */ - oldtuple = slot->val; - - /* ---------------- - * free the old contents of the specified slot if necessary. - * ---------------- - */ - if (slot->ttc_shouldFree && oldtuple != NULL) { + HeapTuple oldtuple; /* prior contents of slot */ + /* ---------------- - * since a tuple may contain a pointer to - * lock information allocated along with the - * tuple, we have to be careful to free any - * rule locks also -cim 1/17/90 + * sanity checks * ---------------- */ - pfree(oldtuple); - } - - /* ---------------- - * store NULL into the specified slot and return the slot. - * - also set buffer to InvalidBuffer -cim 3/14/91 - * ---------------- - */ - slot->val = (HeapTuple)NULL; - - if (BufferIsValid(slot->ttc_buffer)) - ReleaseBuffer(slot->ttc_buffer); - - slot->ttc_buffer = InvalidBuffer; - slot->ttc_shouldFree = true; - - return slot; + Assert(slot != NULL); + + /* ---------------- + * get information from the tuple table + * ---------------- + */ + oldtuple = slot->val; + + /* ---------------- + * free the old contents of the specified slot if necessary. + * ---------------- + */ + if (slot->ttc_shouldFree && oldtuple != NULL) + { + /* ---------------- + * since a tuple may contain a pointer to + * lock information allocated along with the + * tuple, we have to be careful to free any + * rule locks also -cim 1/17/90 + * ---------------- + */ + pfree(oldtuple); + } + + /* ---------------- + * store NULL into the specified slot and return the slot. + * - also set buffer to InvalidBuffer -cim 3/14/91 + * ---------------- + */ + slot->val = (HeapTuple) NULL; + + if (BufferIsValid(slot->ttc_buffer)) + ReleaseBuffer(slot->ttc_buffer); + + slot->ttc_buffer = InvalidBuffer; + slot->ttc_shouldFree = true; + + return slot; } /* -------------------------------- - * ExecSlotPolicy + * ExecSlotPolicy * - * This function is used to get the call/don't call pfree - * setting of a slot. Most executor routines don't need this. - * It's only when you do tricky things like marking tuples for - * merge joins that you need to diddle the slot policy. + * This function is used to get the call/don't call pfree + * setting of a slot. Most executor routines don't need this. + * It's only when you do tricky things like marking tuples for + * merge joins that you need to diddle the slot policy. * -------------------------------- */ #ifdef NOT_USED -bool /* return: slot policy */ -ExecSlotPolicy(TupleTableSlot* slot) /* slot to inspect */ +bool /* return: slot policy */ +ExecSlotPolicy(TupleTableSlot * slot) /* slot to inspect */ { - return slot->ttc_shouldFree; + return slot->ttc_shouldFree; } + #endif /* -------------------------------- - * ExecSetSlotPolicy + * ExecSetSlotPolicy * - * This function is used to change the call/don't call pfree - * setting of a slot. Most executor routines don't need this. - * It's only when you do tricky things like marking tuples for - * merge joins that you need to diddle the slot policy. + * This function is used to change the call/don't call pfree + * setting of a slot. Most executor routines don't need this. + * It's only when you do tricky things like marking tuples for + * merge joins that you need to diddle the slot policy. * -------------------------------- */ -bool /* return: old slot policy */ -ExecSetSlotPolicy(TupleTableSlot* slot, /* slot to change */ - bool shouldFree) /* true if we call pfree() when we gc. */ +bool /* return: old slot policy */ +ExecSetSlotPolicy(TupleTableSlot * slot, /* slot to change */ + bool shouldFree) /* true if we call pfree() when we + * gc. */ { - bool old_shouldFree = slot->ttc_shouldFree; - slot->ttc_shouldFree = shouldFree; + bool old_shouldFree = slot->ttc_shouldFree; - return old_shouldFree; + slot->ttc_shouldFree = shouldFree; + + return old_shouldFree; } /* -------------------------------- - * ExecSlotDescriptor + * ExecSlotDescriptor * - * This function is used to get the tuple descriptor associated - * with the slot's tuple. + * This function is used to get the tuple descriptor associated + * with the slot's tuple. * * Now a macro in tuptable.h -mer 5 March 1992 * -------------------------------- */ /* -------------------------------- - * ExecSetSlotDescriptor + * ExecSetSlotDescriptor * - * This function is used to set the tuple descriptor associated - * with the slot's tuple. + * This function is used to set the tuple descriptor associated + * with the slot's tuple. * -------------------------------- */ -TupleDesc /* return: old slot tuple descriptor */ -ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */ - TupleDesc tupdesc) /* tuple descriptor */ +TupleDesc /* return: old slot tuple descriptor */ +ExecSetSlotDescriptor(TupleTableSlot * slot, /* slot to change */ + TupleDesc tupdesc) /* tuple descriptor */ { - TupleDesc old_tupdesc = slot->ttc_tupleDescriptor; + TupleDesc old_tupdesc = slot->ttc_tupleDescriptor; - slot->ttc_tupleDescriptor = tupdesc; - return old_tupdesc; + slot->ttc_tupleDescriptor = tupdesc; + return old_tupdesc; } /* -------------------------------- - * ExecSetSlotDescriptorIsNew + * ExecSetSlotDescriptorIsNew * - * This function is used to change the setting of the "isNew" flag + * This function is used to change the setting of the "isNew" flag * -------------------------------- */ void -ExecSetSlotDescriptorIsNew(TupleTableSlot *slot,/* slot to change */ - bool isNew) /* "isNew" setting */ +ExecSetSlotDescriptorIsNew(TupleTableSlot * slot, /* slot to change */ + bool isNew) /* "isNew" setting */ { - slot->ttc_descIsNew = isNew; + slot->ttc_descIsNew = isNew; } /* -------------------------------- - * ExecSetNewSlotDescriptor + * ExecSetNewSlotDescriptor * - * This function is used to set the tuple descriptor associated - * with the slot's tuple, and set the "isNew" flag at the same time. + * This function is used to set the tuple descriptor associated + * with the slot's tuple, and set the "isNew" flag at the same time. * -------------------------------- */ #ifdef NOT_USED -TupleDesc /* return: old slot tuple descriptor */ -ExecSetNewSlotDescriptor(TupleTableSlot *slot, /* slot to change */ - TupleDesc tupdesc) /* tuple descriptor */ +TupleDesc /* return: old slot tuple descriptor */ +ExecSetNewSlotDescriptor(TupleTableSlot * slot, /* slot to change */ + TupleDesc tupdesc) /* tuple descriptor */ { - TupleDesc old_tupdesc = slot->ttc_tupleDescriptor; - slot->ttc_tupleDescriptor = tupdesc; - slot->ttc_descIsNew = true; - - return old_tupdesc; + TupleDesc old_tupdesc = slot->ttc_tupleDescriptor; + + slot->ttc_tupleDescriptor = tupdesc; + slot->ttc_descIsNew = true; + + return old_tupdesc; } + #endif /* -------------------------------- - * ExecSlotBuffer + * ExecSlotBuffer * - * This function is used to get the tuple descriptor associated - * with the slot's tuple. Be very careful with this as it does not - * balance the reference counts. If the buffer returned is stored - * someplace else, then also use ExecIncrSlotBufferRefcnt(). + * This function is used to get the tuple descriptor associated + * with the slot's tuple. Be very careful with this as it does not + * balance the reference counts. If the buffer returned is stored + * someplace else, then also use ExecIncrSlotBufferRefcnt(). * * Now a macro in tuptable.h * -------------------------------- */ /* -------------------------------- - * ExecSetSlotBuffer + * ExecSetSlotBuffer * - * This function is used to set the tuple descriptor associated - * with the slot's tuple. Be very careful with this as it does not - * balance the reference counts. If we're using this then we should - * also use ExecIncrSlotBufferRefcnt(). + * This function is used to set the tuple descriptor associated + * with the slot's tuple. Be very careful with this as it does not + * balance the reference counts. If we're using this then we should + * also use ExecIncrSlotBufferRefcnt(). * -------------------------------- */ #ifdef NOT_USED -Buffer /* return: old slot buffer */ -ExecSetSlotBuffer(TupleTableSlot *slot, /* slot to change */ - Buffer b) /* tuple descriptor */ +Buffer /* return: old slot buffer */ +ExecSetSlotBuffer(TupleTableSlot * slot, /* slot to change */ + Buffer b) /* tuple descriptor */ { - Buffer oldb = slot->ttc_buffer; - slot->ttc_buffer = b; - - return oldb; + Buffer oldb = slot->ttc_buffer; + + slot->ttc_buffer = b; + + return oldb; } + #endif /* -------------------------------- - * ExecIncrSlotBufferRefcnt + * ExecIncrSlotBufferRefcnt * - * When we pass around buffers in the tuple table, we have to - * be careful to increment reference counts appropriately. - * This is used mainly in the mergejoin code. + * When we pass around buffers in the tuple table, we have to + * be careful to increment reference counts appropriately. + * This is used mainly in the mergejoin code. * -------------------------------- */ void -ExecIncrSlotBufferRefcnt(TupleTableSlot *slot) /* slot to bump refcnt */ +ExecIncrSlotBufferRefcnt(TupleTableSlot * slot) /* slot to bump refcnt */ { -/* Buffer b = SlotBuffer((TupleTableSlot*) slot); */ - Buffer b = slot->ttc_buffer; - if (BufferIsValid(b)) - IncrBufferRefCount(b); +/* Buffer b = SlotBuffer((TupleTableSlot*) slot); */ + Buffer b = slot->ttc_buffer; + + if (BufferIsValid(b)) + IncrBufferRefCount(b); } /* ---------------------------------------------------------------- - * tuple table slot status predicates + * tuple table slot status predicates * ---------------------------------------------------------------- */ /* ---------------- - * TupIsNull + * TupIsNull * - * This is used mainly to detect when there are no more - * tuples to process. + * This is used mainly to detect when there are no more + * tuples to process. * ---------------- */ -bool /* return: true if tuple in slot is NULL */ -TupIsNull(TupleTableSlot* slot) /* slot to check */ +bool /* return: true if tuple in slot is NULL */ +TupIsNull(TupleTableSlot * slot) /* slot to check */ { - HeapTuple tuple; /* contents of slot (returned) */ - - /* ---------------- - * if the slot itself is null then we return true - * ---------------- - */ - if (slot == NULL) - return true; - - /* ---------------- - * get information from the slot and return true or - * false depending on the contents of the slot. - * ---------------- - */ - tuple = slot->val; - - return - (tuple == NULL ? true : false); + HeapTuple tuple; /* contents of slot (returned) */ + + /* ---------------- + * if the slot itself is null then we return true + * ---------------- + */ + if (slot == NULL) + return true; + + /* ---------------- + * get information from the slot and return true or + * false depending on the contents of the slot. + * ---------------- + */ + tuple = slot->val; + + return + (tuple == NULL ? true : false); } /* -------------------------------- - * ExecSlotDescriptorIsNew + * ExecSlotDescriptorIsNew * - * This function is used to check if the tuple descriptor - * associated with this slot has just changed. ie: we are - * now storing a new type of tuple in this slot + * This function is used to check if the tuple descriptor + * associated with this slot has just changed. ie: we are + * now storing a new type of tuple in this slot * -------------------------------- */ #ifdef NOT_USED -bool /* return: descriptor "is new" */ -ExecSlotDescriptorIsNew(TupleTableSlot *slot) /* slot to inspect */ +bool /* return: descriptor "is new" */ +ExecSlotDescriptorIsNew(TupleTableSlot * slot) /* slot to inspect */ { -/* bool isNew = SlotTupleDescriptorIsNew((TupleTableSlot*) slot); - return isNew; */ - return slot->ttc_descIsNew; +/* bool isNew = SlotTupleDescriptorIsNew((TupleTableSlot*) slot); + return isNew; */ + return slot->ttc_descIsNew; } + #endif /* ---------------------------------------------------------------- - * convenience initialization routines + * convenience initialization routines * ---------------------------------------------------------------- */ /* -------------------------------- - * ExecInit{Result,Scan,Raw,Marked,Outer,Hash}TupleSlot + * ExecInit{Result,Scan,Raw,Marked,Outer,Hash}TupleSlot * - * These are convenience routines to initialize the specfied slot - * in nodes inheriting the appropriate state. + * These are convenience routines to initialize the specfied slot + * in nodes inheriting the appropriate state. * -------------------------------- */ #define INIT_SLOT_DEFS \ - TupleTable tupleTable; \ - TupleTableSlot* slot - + TupleTable tupleTable; \ + TupleTableSlot* slot + #define INIT_SLOT_ALLOC \ - tupleTable = (TupleTable) estate->es_tupleTable; \ - slot = ExecAllocTableSlot(tupleTable); \ - slot->val = (HeapTuple)NULL; \ - slot->ttc_shouldFree = true; \ - slot->ttc_tupleDescriptor = (TupleDesc)NULL; \ - slot->ttc_whichplan = -1;\ - slot->ttc_descIsNew = true; + tupleTable = (TupleTable) estate->es_tupleTable; \ + slot = ExecAllocTableSlot(tupleTable); \ + slot->val = (HeapTuple)NULL; \ + slot->ttc_shouldFree = true; \ + slot->ttc_tupleDescriptor = (TupleDesc)NULL; \ + slot->ttc_whichplan = -1;\ + slot->ttc_descIsNew = true; /* ---------------- - * ExecInitResultTupleSlot + * ExecInitResultTupleSlot * ---------------- */ void -ExecInitResultTupleSlot(EState *estate, CommonState *commonstate) +ExecInitResultTupleSlot(EState * estate, CommonState * commonstate) { - INIT_SLOT_DEFS; - INIT_SLOT_ALLOC; - commonstate->cs_ResultTupleSlot = (TupleTableSlot *) slot; + INIT_SLOT_DEFS; + INIT_SLOT_ALLOC; + commonstate->cs_ResultTupleSlot = (TupleTableSlot *) slot; } /* ---------------- - * ExecInitScanTupleSlot + * ExecInitScanTupleSlot * ---------------- */ void -ExecInitScanTupleSlot(EState *estate, CommonScanState *commonscanstate) +ExecInitScanTupleSlot(EState * estate, CommonScanState * commonscanstate) { - INIT_SLOT_DEFS; - INIT_SLOT_ALLOC; - commonscanstate->css_ScanTupleSlot = (TupleTableSlot *)slot; + INIT_SLOT_DEFS; + INIT_SLOT_ALLOC; + commonscanstate->css_ScanTupleSlot = (TupleTableSlot *) slot; } /* ---------------- - * ExecInitMarkedTupleSlot + * ExecInitMarkedTupleSlot * ---------------- */ void -ExecInitMarkedTupleSlot(EState *estate, MergeJoinState *mergestate) +ExecInitMarkedTupleSlot(EState * estate, MergeJoinState * mergestate) { - INIT_SLOT_DEFS; - INIT_SLOT_ALLOC; - mergestate->mj_MarkedTupleSlot = (TupleTableSlot *) slot; + INIT_SLOT_DEFS; + INIT_SLOT_ALLOC; + mergestate->mj_MarkedTupleSlot = (TupleTableSlot *) slot; } /* ---------------- - * ExecInitOuterTupleSlot + * ExecInitOuterTupleSlot * ---------------- */ void -ExecInitOuterTupleSlot(EState *estate, HashJoinState *hashstate) +ExecInitOuterTupleSlot(EState * estate, HashJoinState * hashstate) { - INIT_SLOT_DEFS; - INIT_SLOT_ALLOC; - hashstate->hj_OuterTupleSlot = slot; + INIT_SLOT_DEFS; + INIT_SLOT_ALLOC; + hashstate->hj_OuterTupleSlot = slot; } /* ---------------- - * ExecInitHashTupleSlot + * ExecInitHashTupleSlot * ---------------- */ #ifdef NOT_USED void -ExecInitHashTupleSlot(EState *estate, HashJoinState *hashstate) +ExecInitHashTupleSlot(EState * estate, HashJoinState * hashstate) { - INIT_SLOT_DEFS; - INIT_SLOT_ALLOC; - hashstate->hj_HashTupleSlot = slot; + INIT_SLOT_DEFS; + INIT_SLOT_ALLOC; + hashstate->hj_HashTupleSlot = slot; } + #endif static TupleTableSlot * -NodeGetResultTupleSlot(Plan *node) +NodeGetResultTupleSlot(Plan * node) { - TupleTableSlot *slot; - - switch(nodeTag(node)) { - - case T_Result: - { - ResultState *resstate = ((Result *)node)->resstate; - slot = resstate->cstate.cs_ResultTupleSlot; - } - break; - - case T_SeqScan: - { - CommonScanState *scanstate = ((SeqScan *)node)->scanstate; - slot = scanstate->cstate.cs_ResultTupleSlot; - } - break; - - case T_NestLoop: - { - NestLoopState *nlstate = ((NestLoop *)node)->nlstate; - slot = nlstate->jstate.cs_ResultTupleSlot; - } - break; - - case T_Append: - { - Append *n = (Append *)node; - AppendState *unionstate; - List *unionplans; - int whichplan; - Plan *subplan; - - unionstate = n->unionstate; - unionplans = n->unionplans; - whichplan = unionstate->as_whichplan; - - subplan = (Plan*) nth(whichplan, unionplans); - slot = NodeGetResultTupleSlot(subplan); - break; - } - - case T_IndexScan: - { - CommonScanState *scanstate = ((IndexScan *)node)->scan.scanstate; - slot = scanstate->cstate.cs_ResultTupleSlot; - } - break; - - case T_Material: - { - MaterialState *matstate = ((Material *)node)->matstate; - slot = matstate->csstate.css_ScanTupleSlot; - } - break; - - case T_Sort: - { - SortState *sortstate = ((Sort *)node)->sortstate; - slot = sortstate->csstate.css_ScanTupleSlot; - } - break; - - case T_Agg: - { - AggState *aggstate = ((Agg *)node)->aggstate; - slot = aggstate->csstate.cstate.cs_ResultTupleSlot; - } - break; - - case T_Group: - { - GroupState *grpstate = ((Group *)node)->grpstate; - slot = grpstate->csstate.cstate.cs_ResultTupleSlot; - } - break; - - case T_Hash: - { - HashState *hashstate = ((Hash *)node)->hashstate; - slot = hashstate->cstate.cs_ResultTupleSlot; - } - break; - - case T_Unique: - { - UniqueState *uniquestate = ((Unique *)node)->uniquestate; - slot = uniquestate->cs_ResultTupleSlot; - } - break; - - case T_MergeJoin: - { - MergeJoinState *mergestate = ((MergeJoin *)node)->mergestate; - slot = mergestate->jstate.cs_ResultTupleSlot; - } - break; - - case T_HashJoin: + TupleTableSlot *slot; + + switch (nodeTag(node)) { - HashJoinState *hashjoinstate = ((HashJoin *)node)->hashjoinstate; - slot = hashjoinstate->jstate.cs_ResultTupleSlot; + + case T_Result: + { + ResultState *resstate = ((Result *) node)->resstate; + + slot = resstate->cstate.cs_ResultTupleSlot; + } + break; + + case T_SeqScan: + { + CommonScanState *scanstate = ((SeqScan *) node)->scanstate; + + slot = scanstate->cstate.cs_ResultTupleSlot; + } + break; + + case T_NestLoop: + { + NestLoopState *nlstate = ((NestLoop *) node)->nlstate; + + slot = nlstate->jstate.cs_ResultTupleSlot; + } + break; + + case T_Append: + { + Append *n = (Append *) node; + AppendState *unionstate; + List *unionplans; + int whichplan; + Plan *subplan; + + unionstate = n->unionstate; + unionplans = n->unionplans; + whichplan = unionstate->as_whichplan; + + subplan = (Plan *) nth(whichplan, unionplans); + slot = NodeGetResultTupleSlot(subplan); + break; + } + + case T_IndexScan: + { + CommonScanState *scanstate = ((IndexScan *) node)->scan.scanstate; + + slot = scanstate->cstate.cs_ResultTupleSlot; + } + break; + + case T_Material: + { + MaterialState *matstate = ((Material *) node)->matstate; + + slot = matstate->csstate.css_ScanTupleSlot; + } + break; + + case T_Sort: + { + SortState *sortstate = ((Sort *) node)->sortstate; + + slot = sortstate->csstate.css_ScanTupleSlot; + } + break; + + case T_Agg: + { + AggState *aggstate = ((Agg *) node)->aggstate; + + slot = aggstate->csstate.cstate.cs_ResultTupleSlot; + } + break; + + case T_Group: + { + GroupState *grpstate = ((Group *) node)->grpstate; + + slot = grpstate->csstate.cstate.cs_ResultTupleSlot; + } + break; + + case T_Hash: + { + HashState *hashstate = ((Hash *) node)->hashstate; + + slot = hashstate->cstate.cs_ResultTupleSlot; + } + break; + + case T_Unique: + { + UniqueState *uniquestate = ((Unique *) node)->uniquestate; + + slot = uniquestate->cs_ResultTupleSlot; + } + break; + + case T_MergeJoin: + { + MergeJoinState *mergestate = ((MergeJoin *) node)->mergestate; + + slot = mergestate->jstate.cs_ResultTupleSlot; + } + break; + + case T_HashJoin: + { + HashJoinState *hashjoinstate = ((HashJoin *) node)->hashjoinstate; + + slot = hashjoinstate->jstate.cs_ResultTupleSlot; + } + break; + + case T_Tee: + { + TeeState *teestate = ((Tee *) node)->teestate; + + slot = teestate->cstate.cs_ResultTupleSlot; + } + break; + + default: + /* ---------------- + * should never get here + * ---------------- + */ + elog(WARN, "NodeGetResultTupleSlot: node not yet supported: %d ", + nodeTag(node)); + + return NULL; } - break; - - case T_Tee: - { - TeeState *teestate = ((Tee*)node)->teestate; - slot = teestate->cstate.cs_ResultTupleSlot; - } - break; - - default: - /* ---------------- - * should never get here - * ---------------- - */ - elog(WARN, "NodeGetResultTupleSlot: node not yet supported: %d ", - nodeTag(node)); - - return NULL; - } - return slot; + return slot; } /* ---------------------------------------------------------------- - * ExecGetTupType + * ExecGetTupType * - * this gives you the tuple descriptor for tuples returned - * by this node. I really wish I could ditch this routine, - * but since not all nodes store their type info in the same - * place, we have to do something special for each node type. + * this gives you the tuple descriptor for tuples returned + * by this node. I really wish I could ditch this routine, + * but since not all nodes store their type info in the same + * place, we have to do something special for each node type. * - * Soon, the system will have to adapt to deal with changing - * tuple descriptors as we deal with dynamic tuple types - * being returned from procedure nodes. Perhaps then this - * routine can be retired. -cim 6/3/91 + * Soon, the system will have to adapt to deal with changing + * tuple descriptors as we deal with dynamic tuple types + * being returned from procedure nodes. Perhaps then this + * routine can be retired. -cim 6/3/91 * * old comments - * This routine just gets the type information out of the - * node's state. If you already have a node's state, you - * can get this information directly, but this is a useful - * routine if you want to get the type information from - * the node's inner or outer subplan easily without having - * to inspect the subplan.. -cim 10/16/89 + * This routine just gets the type information out of the + * node's state. If you already have a node's state, you + * can get this information directly, but this is a useful + * routine if you want to get the type information from + * the node's inner or outer subplan easily without having + * to inspect the subplan.. -cim 10/16/89 * - * Assume that for existential nodes, we get the targetlist out - * of the right node's targetlist + * Assume that for existential nodes, we get the targetlist out + * of the right node's targetlist * ---------------------------------------------------------------- */ TupleDesc -ExecGetTupType(Plan *node) +ExecGetTupType(Plan * node) { - TupleTableSlot *slot; - TupleDesc tupType; - - if (node == NULL) - return NULL; - - slot = NodeGetResultTupleSlot(node); - tupType = slot->ttc_tupleDescriptor; - return tupType; + TupleTableSlot *slot; + TupleDesc tupType; + + if (node == NULL) + return NULL; + + slot = NodeGetResultTupleSlot(node); + tupType = slot->ttc_tupleDescriptor; + return tupType; } /* TupleDesc -ExecCopyTupType(TupleDesc td, int natts) +ExecCopyTupType(TupleDesc td, int natts) { - TupleDesc newTd; - int i; - - newTd = CreateTemplateTupleDesc(natts); - i = 0; - while (i < natts) - { - newTd[i] = - (AttributeTupleForm)palloc(sizeof(FormData_pg_attribute)); - memmove(newTd[i], td[i], sizeof(FormData_pg_attribute)); - i++; - } - return newTd; + TupleDesc newTd; + int i; + + newTd = CreateTemplateTupleDesc(natts); + i = 0; + while (i < natts) + { + newTd[i] = + (AttributeTupleForm)palloc(sizeof(FormData_pg_attribute)); + memmove(newTd[i], td[i], sizeof(FormData_pg_attribute)); + i++; + } + return newTd; } */ /* ---------------------------------------------------------------- - * ExecTypeFromTL - * - * Currently there are about 4 different places where we create - * TupleDescriptors. They should all be merged, or perhaps - * be rewritten to call BuildDesc(). - * - * old comments - * Forms attribute type info from the target list in the node. - * It assumes all domains are individually specified in the target list. - * It fails if the target list contains something like Emp.all - * which represents all the attributes from EMP relation. - * - * Conditions: - * The inner and outer subtrees should be initialized because it - * might be necessary to know the type infos of the subtrees. + * ExecTypeFromTL + * + * Currently there are about 4 different places where we create + * TupleDescriptors. They should all be merged, or perhaps + * be rewritten to call BuildDesc(). + * + * old comments + * Forms attribute type info from the target list in the node. + * It assumes all domains are individually specified in the target list. + * It fails if the target list contains something like Emp.all + * which represents all the attributes from EMP relation. + * + * Conditions: + * The inner and outer subtrees should be initialized because it + * might be necessary to know the type infos of the subtrees. * ---------------------------------------------------------------- */ TupleDesc -ExecTypeFromTL(List *targetList) +ExecTypeFromTL(List * targetList) { - List *tlcdr; - TupleDesc typeInfo; - Resdom *resdom; - Oid restype; - int len; - - /* ---------------- - * examine targetlist - if empty then return NULL - * ---------------- - */ - len = ExecTargetListLength(targetList); - - if (len == 0) - return NULL; - - /* ---------------- - * allocate a new typeInfo - * ---------------- - */ - typeInfo = CreateTemplateTupleDesc(len); - - /* ---------------- - * notes: get resdom from (resdom expr) - * get_typbyval comes from src/lib/l-lisp/lsyscache.c - * ---------------- - */ - tlcdr = targetList; - while (tlcdr != NIL) { - TargetEntry *tle = lfirst(tlcdr); - if (tle->resdom != NULL) { - resdom = tle->resdom; - restype = resdom->restype; - - TupleDescInitEntry(typeInfo, - resdom->resno, - resdom->resname, - /* fix for SELECT NULL ... */ - get_id_typname(restype?restype:UNKNOWNOID), - 0, - false); + List *tlcdr; + TupleDesc typeInfo; + Resdom *resdom; + Oid restype; + int len; + + /* ---------------- + * examine targetlist - if empty then return NULL + * ---------------- + */ + len = ExecTargetListLength(targetList); + + if (len == 0) + return NULL; + + /* ---------------- + * allocate a new typeInfo + * ---------------- + */ + typeInfo = CreateTemplateTupleDesc(len); + + /* ---------------- + * notes: get resdom from (resdom expr) + * get_typbyval comes from src/lib/l-lisp/lsyscache.c + * ---------------- + */ + tlcdr = targetList; + while (tlcdr != NIL) + { + TargetEntry *tle = lfirst(tlcdr); + + if (tle->resdom != NULL) + { + resdom = tle->resdom; + restype = resdom->restype; + + TupleDescInitEntry(typeInfo, + resdom->resno, + resdom->resname, + /* fix for SELECT NULL ... */ + get_id_typname(restype ? restype : UNKNOWNOID), + 0, + false); /* - ExecSetTypeInfo(resdom->resno - 1, - typeInfo, - (Oid) restype, - resdom->resno, - resdom->reslen, - resdom->resname->data, - get_typbyval(restype), - get_typalign(restype)); + ExecSetTypeInfo(resdom->resno - 1, + typeInfo, + (Oid) restype, + resdom->resno, + resdom->reslen, + resdom->resname->data, + get_typbyval(restype), + get_typalign(restype)); */ - } - else { - Resdom *fjRes; - List *fjTlistP; - List *fjList = lfirst(tlcdr); + } + else + { + Resdom *fjRes; + List *fjTlistP; + List *fjList = lfirst(tlcdr); + #ifdef SETS_FIXED - TargetEntry *tle; - Fjoin *fjNode = ((TargetEntry *)lfirst(fjList))->fjoin; + TargetEntry *tle; + Fjoin *fjNode = ((TargetEntry *) lfirst(fjList))->fjoin; - tle = fjNode->fj_innerNode; /* ??? */ + tle = fjNode->fj_innerNode; /* ??? */ #endif - fjRes = tle->resdom; - restype = fjRes->restype; - - TupleDescInitEntry(typeInfo, - fjRes->resno, - fjRes->resname, - get_id_typname(restype), - 0, - false); + fjRes = tle->resdom; + restype = fjRes->restype; + + TupleDescInitEntry(typeInfo, + fjRes->resno, + fjRes->resname, + get_id_typname(restype), + 0, + false); /* - ExecSetTypeInfo(fjRes->resno - 1, - typeInfo, - (Oid) restype, - fjRes->resno, - fjRes->reslen, - (char *) fjRes->resname, - get_typbyval(restype), - get_typalign(restype)); + ExecSetTypeInfo(fjRes->resno - 1, + typeInfo, + (Oid) restype, + fjRes->resno, + fjRes->reslen, + (char *) fjRes->resname, + get_typbyval(restype), + get_typalign(restype)); */ - - foreach(fjTlistP, lnext(fjList)) { - TargetEntry *fjTle = lfirst(fjTlistP); - - fjRes = fjTle->resdom; - - TupleDescInitEntry(typeInfo, - fjRes->resno, - fjRes->resname, - get_id_typname(restype), - 0, - false); - + + foreach(fjTlistP, lnext(fjList)) + { + TargetEntry *fjTle = lfirst(fjTlistP); + + fjRes = fjTle->resdom; + + TupleDescInitEntry(typeInfo, + fjRes->resno, + fjRes->resname, + get_id_typname(restype), + 0, + false); + /* - ExecSetTypeInfo(fjRes->resno - 1, - typeInfo, - (Oid) fjRes->restype, - fjRes->resno, - fjRes->reslen, - (char *) fjRes->resname, - get_typbyval(fjRes->restype), - get_typalign(fjRes->restype)); + ExecSetTypeInfo(fjRes->resno - 1, + typeInfo, + (Oid) fjRes->restype, + fjRes->resno, + fjRes->reslen, + (char *) fjRes->resname, + get_typbyval(fjRes->restype), + get_typalign(fjRes->restype)); */ - } - } - - tlcdr = lnext(tlcdr); - } - - return typeInfo; -} + } + } + tlcdr = lnext(tlcdr); + } + return typeInfo; +} diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index ac2d3516036..3795c2d1018 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -1,43 +1,43 @@ /*------------------------------------------------------------------------- * * execUtils.c-- - * miscellanious executor utility routines + * miscellanious executor utility routines * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.14 1997/08/22 03:12:19 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.15 1997/09/07 04:41:26 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * ExecAssignNodeBaseInfo \ - * ExecAssignDebugHooks > preforms misc work done in all the - * ExecAssignExprContext / init node routines. + * ExecAssignNodeBaseInfo \ + * ExecAssignDebugHooks > preforms misc work done in all the + * ExecAssignExprContext / init node routines. * - * ExecGetTypeInfo | old execCStructs interface - * ExecMakeTypeInfo | code from the version 1 - * ExecOrderTypeInfo | lisp system. These should - * ExecSetTypeInfo | go away or be updated soon. - * ExecFreeTypeInfo | -cim 11/1/89 - * ExecTupleAttributes / + * ExecGetTypeInfo | old execCStructs interface + * ExecMakeTypeInfo | code from the version 1 + * ExecOrderTypeInfo | lisp system. These should + * ExecSetTypeInfo | go away or be updated soon. + * ExecFreeTypeInfo | -cim 11/1/89 + * ExecTupleAttributes / * - * QueryDescGetTypeInfo - moved here from main.c - * am not sure what uses it -cim 10/12/89 + * QueryDescGetTypeInfo - moved here from main.c + * am not sure what uses it -cim 10/12/89 * - * ExecGetIndexKeyInfo \ - * ExecOpenIndices | referenced by InitPlan, EndPlan, - * ExecCloseIndices | ExecAppend, ExecReplace - * ExecFormIndexTuple | - * ExecInsertIndexTuple / + * ExecGetIndexKeyInfo \ + * ExecOpenIndices | referenced by InitPlan, EndPlan, + * ExecCloseIndices | ExecAppend, ExecReplace + * ExecFormIndexTuple | + * ExecInsertIndexTuple / + * + * NOTES + * This file has traditionally been the place to stick misc. + * executor support stuff that doesn't really go anyplace else. * - * NOTES - * This file has traditionally been the place to stick misc. - * executor support stuff that doesn't really go anyplace else. - * */ #include "postgres.h" @@ -58,1149 +58,1195 @@ #include "catalog/pg_type.h" #include "parser/parsetree.h" -static void ExecGetIndexKeyInfo(IndexTupleForm indexTuple, int *numAttsOutP, - AttrNumber **attsOutP, FuncIndexInfoPtr fInfoP); +static void +ExecGetIndexKeyInfo(IndexTupleForm indexTuple, int *numAttsOutP, + AttrNumber ** attsOutP, FuncIndexInfoPtr fInfoP); /* ---------------------------------------------------------------- - * global counters for number of tuples processed, retrieved, - * appended, replaced, deleted. + * global counters for number of tuples processed, retrieved, + * appended, replaced, deleted. * ---------------------------------------------------------------- */ -int NTupleProcessed; -int NTupleRetrieved; -int NTupleReplaced; -int NTupleAppended; -int NTupleDeleted; -int NIndexTupleInserted; -extern int NIndexTupleProcessed; /* have to be defined in the access - method level so that the cinterface.a - will link ok. */ +int NTupleProcessed; +int NTupleRetrieved; +int NTupleReplaced; +int NTupleAppended; +int NTupleDeleted; +int NIndexTupleInserted; +extern int NIndexTupleProcessed; /* have to be defined in the + * access method level so that the + * cinterface.a will link ok. */ /* ---------------------------------------------------------------- - * statistic functions + * statistic functions * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- - * ResetTupleCount + * ResetTupleCount * ---------------------------------------------------------------- */ #ifdef NOT_USED void ResetTupleCount(void) { - NTupleProcessed = 0; - NTupleRetrieved = 0; - NTupleAppended = 0; - NTupleDeleted = 0; - NTupleReplaced = 0; - NIndexTupleProcessed = 0; + NTupleProcessed = 0; + NTupleRetrieved = 0; + NTupleAppended = 0; + NTupleDeleted = 0; + NTupleReplaced = 0; + NIndexTupleProcessed = 0; } + #endif /* ---------------------------------------------------------------- - * PrintTupleCount + * PrintTupleCount * ---------------------------------------------------------------- */ #ifdef NOT_USED void -DisplayTupleCount(FILE *statfp) +DisplayTupleCount(FILE * statfp) { - if (NTupleProcessed > 0) - fprintf(statfp, "!\t%d tuple%s processed, ", NTupleProcessed, - (NTupleProcessed == 1) ? "" : "s"); - else { - fprintf(statfp, "!\tno tuples processed.\n"); - return; - } - if (NIndexTupleProcessed > 0) - fprintf(statfp, "%d indextuple%s processed, ", NIndexTupleProcessed, - (NIndexTupleProcessed == 1) ? "" : "s"); - if (NIndexTupleInserted > 0) - fprintf(statfp, "%d indextuple%s inserted, ", NIndexTupleInserted, - (NIndexTupleInserted == 1) ? "" : "s"); - if (NTupleRetrieved > 0) - fprintf(statfp, "%d tuple%s retrieved. ", NTupleRetrieved, - (NTupleRetrieved == 1) ? "" : "s"); - if (NTupleAppended > 0) - fprintf(statfp, "%d tuple%s appended. ", NTupleAppended, - (NTupleAppended == 1) ? "" : "s"); - if (NTupleDeleted > 0) - fprintf(statfp, "%d tuple%s deleted. ", NTupleDeleted, - (NTupleDeleted == 1) ? "" : "s"); - if (NTupleReplaced > 0) - fprintf(statfp, "%d tuple%s replaced. ", NTupleReplaced, - (NTupleReplaced == 1) ? "" : "s"); - fprintf(statfp, "\n"); + if (NTupleProcessed > 0) + fprintf(statfp, "!\t%d tuple%s processed, ", NTupleProcessed, + (NTupleProcessed == 1) ? "" : "s"); + else + { + fprintf(statfp, "!\tno tuples processed.\n"); + return; + } + if (NIndexTupleProcessed > 0) + fprintf(statfp, "%d indextuple%s processed, ", NIndexTupleProcessed, + (NIndexTupleProcessed == 1) ? "" : "s"); + if (NIndexTupleInserted > 0) + fprintf(statfp, "%d indextuple%s inserted, ", NIndexTupleInserted, + (NIndexTupleInserted == 1) ? "" : "s"); + if (NTupleRetrieved > 0) + fprintf(statfp, "%d tuple%s retrieved. ", NTupleRetrieved, + (NTupleRetrieved == 1) ? "" : "s"); + if (NTupleAppended > 0) + fprintf(statfp, "%d tuple%s appended. ", NTupleAppended, + (NTupleAppended == 1) ? "" : "s"); + if (NTupleDeleted > 0) + fprintf(statfp, "%d tuple%s deleted. ", NTupleDeleted, + (NTupleDeleted == 1) ? "" : "s"); + if (NTupleReplaced > 0) + fprintf(statfp, "%d tuple%s replaced. ", NTupleReplaced, + (NTupleReplaced == 1) ? "" : "s"); + fprintf(statfp, "\n"); } + #endif /* ---------------------------------------------------------------- - * miscellanious init node support functions + * miscellanious init node support functions * - * ExecAssignNodeBaseInfo - assigns the baseid field of the node - * ExecAssignDebugHooks - assigns the node's debugging hooks - * ExecAssignExprContext - assigns the node's expression context + * ExecAssignNodeBaseInfo - assigns the baseid field of the node + * ExecAssignDebugHooks - assigns the node's debugging hooks + * ExecAssignExprContext - assigns the node's expression context * ---------------------------------------------------------------- */ /* ---------------- - * ExecAssignNodeBaseInfo + * ExecAssignNodeBaseInfo * - * as it says, this assigns the baseid field of the node and - * increments the counter in the estate. In addition, it initializes - * the base_parent field of the basenode. + * as it says, this assigns the baseid field of the node and + * increments the counter in the estate. In addition, it initializes + * the base_parent field of the basenode. * ---------------- */ void -ExecAssignNodeBaseInfo(EState *estate, CommonState *cstate, Plan *parent) +ExecAssignNodeBaseInfo(EState * estate, CommonState * cstate, Plan * parent) { - int baseId; - - baseId = estate->es_BaseId; - cstate->cs_base_id = baseId; - estate->es_BaseId = baseId + 1; + int baseId; + + baseId = estate->es_BaseId; + cstate->cs_base_id = baseId; + estate->es_BaseId = baseId + 1; } /* ---------------- - * ExecAssignExprContext + * ExecAssignExprContext * - * This initializes the ExprContext field. It is only necessary - * to do this for nodes which use ExecQual or ExecTargetList - * because those routines depend on econtext. Other nodes which - * dont have to evaluate expressions don't need to do this. + * This initializes the ExprContext field. It is only necessary + * to do this for nodes which use ExecQual or ExecTargetList + * because those routines depend on econtext. Other nodes which + * dont have to evaluate expressions don't need to do this. * ---------------- */ void -ExecAssignExprContext(EState *estate, CommonState *commonstate) +ExecAssignExprContext(EState * estate, CommonState * commonstate) { - ExprContext *econtext; - ParamListInfo paraminfo; - List *rangeTable; - - paraminfo = estate->es_param_list_info; - rangeTable = estate->es_range_table; - - econtext = makeNode(ExprContext); - econtext->ecxt_scantuple = NULL; /* scan tuple slot */ - econtext->ecxt_innertuple = NULL; /* inner tuple slot */ - econtext->ecxt_outertuple = NULL; /* outer tuple slot */ - econtext->ecxt_relation = NULL; /* relation */ - econtext->ecxt_relid = 0; /* relid */ - econtext->ecxt_param_list_info = paraminfo; /* param list info */ - econtext->ecxt_range_table = rangeTable; /* range table */ - - commonstate->cs_ExprContext = econtext; + ExprContext *econtext; + ParamListInfo paraminfo; + List *rangeTable; + + paraminfo = estate->es_param_list_info; + rangeTable = estate->es_range_table; + + econtext = makeNode(ExprContext); + econtext->ecxt_scantuple = NULL; /* scan tuple slot */ + econtext->ecxt_innertuple = NULL; /* inner tuple slot */ + econtext->ecxt_outertuple = NULL; /* outer tuple slot */ + econtext->ecxt_relation = NULL; /* relation */ + econtext->ecxt_relid = 0; /* relid */ + econtext->ecxt_param_list_info = paraminfo; /* param list info */ + econtext->ecxt_range_table = rangeTable; /* range table */ + + commonstate->cs_ExprContext = econtext; } /* ---------------------------------------------------------------- - * Result slot tuple type and ProjectionInfo support + * Result slot tuple type and ProjectionInfo support * ---------------------------------------------------------------- */ /* ---------------- - * ExecAssignResultType + * ExecAssignResultType * ---------------- */ void -ExecAssignResultType(CommonState *commonstate, - TupleDesc tupDesc) +ExecAssignResultType(CommonState * commonstate, + TupleDesc tupDesc) { - TupleTableSlot *slot; - - slot = commonstate->cs_ResultTupleSlot; - slot->ttc_tupleDescriptor = tupDesc; + TupleTableSlot *slot; + + slot = commonstate->cs_ResultTupleSlot; + slot->ttc_tupleDescriptor = tupDesc; } /* ---------------- - * ExecAssignResultTypeFromOuterPlan + * ExecAssignResultTypeFromOuterPlan * ---------------- */ void -ExecAssignResultTypeFromOuterPlan(Plan *node, CommonState *commonstate) +ExecAssignResultTypeFromOuterPlan(Plan * node, CommonState * commonstate) { - Plan *outerPlan; - TupleDesc tupDesc; - - outerPlan = outerPlan(node); - tupDesc = ExecGetTupType(outerPlan); - - ExecAssignResultType(commonstate, tupDesc); + Plan *outerPlan; + TupleDesc tupDesc; + + outerPlan = outerPlan(node); + tupDesc = ExecGetTupType(outerPlan); + + ExecAssignResultType(commonstate, tupDesc); } /* ---------------- - * ExecAssignResultTypeFromTL + * ExecAssignResultTypeFromTL * ---------------- */ void -ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate) +ExecAssignResultTypeFromTL(Plan * node, CommonState * commonstate) { - List *targetList; - int i; - int len; - List *tl; - TargetEntry *tle; - List *fjtl; - TupleDesc origTupDesc; - - targetList = node->targetlist; - origTupDesc = ExecTypeFromTL(targetList); - len = ExecTargetListLength(targetList); - - fjtl = NIL; - tl = targetList; - i = 0; - while (tl != NIL || fjtl != NIL) { - if (fjtl != NIL) { - tle = lfirst(fjtl); - fjtl = lnext(fjtl); - } - else { - tle = lfirst(tl); - tl = lnext(tl); - } + List *targetList; + int i; + int len; + List *tl; + TargetEntry *tle; + List *fjtl; + TupleDesc origTupDesc; + + targetList = node->targetlist; + origTupDesc = ExecTypeFromTL(targetList); + len = ExecTargetListLength(targetList); + + fjtl = NIL; + tl = targetList; + i = 0; + while (tl != NIL || fjtl != NIL) + { + if (fjtl != NIL) + { + tle = lfirst(fjtl); + fjtl = lnext(fjtl); + } + else + { + tle = lfirst(tl); + tl = lnext(tl); + } #ifdef SETS_FIXED - if (!tl_is_resdom(tle)) { - Fjoin *fj = (Fjoin *)lfirst(tle); - /* it is a FJoin */ - fjtl = lnext(tle); - tle = fj->fj_innerNode; - } + if (!tl_is_resdom(tle)) + { + Fjoin *fj = (Fjoin *) lfirst(tle); + + /* it is a FJoin */ + fjtl = lnext(tle); + tle = fj->fj_innerNode; + } #endif - i++; - } - if (len > 0) { - ExecAssignResultType(commonstate, - origTupDesc); - } - else - ExecAssignResultType(commonstate, - (TupleDesc)NULL); + i++; + } + if (len > 0) + { + ExecAssignResultType(commonstate, + origTupDesc); + } + else + ExecAssignResultType(commonstate, + (TupleDesc) NULL); } /* ---------------- - * ExecGetResultType + * ExecGetResultType * ---------------- */ TupleDesc -ExecGetResultType(CommonState *commonstate) +ExecGetResultType(CommonState * commonstate) { - TupleTableSlot *slot = commonstate->cs_ResultTupleSlot; - - return slot->ttc_tupleDescriptor; + TupleTableSlot *slot = commonstate->cs_ResultTupleSlot; + + return slot->ttc_tupleDescriptor; } /* ---------------- - * ExecFreeResultType + * ExecFreeResultType * ---------------- */ #ifdef NOT_USED void -ExecFreeResultType(CommonState *commonstate) +ExecFreeResultType(CommonState * commonstate) { - TupleTableSlot *slot; - TupleDesc tupType; - - slot = commonstate->cs_ResultTupleSlot; - tupType = slot->ttc_tupleDescriptor; - -/* ExecFreeTypeInfo(tupType); */ - pfree(tupType); + TupleTableSlot *slot; + TupleDesc tupType; + + slot = commonstate->cs_ResultTupleSlot; + tupType = slot->ttc_tupleDescriptor; + +/* ExecFreeTypeInfo(tupType); */ + pfree(tupType); } + #endif /* ---------------- - * ExecAssignProjectionInfo - forms the projection information from the node's targetlist + * ExecAssignProjectionInfo + forms the projection information from the node's targetlist * ---------------- */ void -ExecAssignProjectionInfo(Plan *node, CommonState *commonstate) +ExecAssignProjectionInfo(Plan * node, CommonState * commonstate) { - ProjectionInfo *projInfo; - List *targetList; - int len; - - targetList = node->targetlist; - len = ExecTargetListLength(targetList); - - projInfo = makeNode(ProjectionInfo); - projInfo->pi_targetlist = targetList; - projInfo->pi_len = len; - projInfo->pi_tupValue = - (len <= 0) ? NULL : (Datum *) palloc(sizeof(Datum) * len); - projInfo->pi_exprContext = commonstate->cs_ExprContext; - projInfo->pi_slot = commonstate->cs_ResultTupleSlot; - - commonstate->cs_ProjInfo = projInfo; + ProjectionInfo *projInfo; + List *targetList; + int len; + + targetList = node->targetlist; + len = ExecTargetListLength(targetList); + + projInfo = makeNode(ProjectionInfo); + projInfo->pi_targetlist = targetList; + projInfo->pi_len = len; + projInfo->pi_tupValue = + (len <= 0) ? NULL : (Datum *) palloc(sizeof(Datum) * len); + projInfo->pi_exprContext = commonstate->cs_ExprContext; + projInfo->pi_slot = commonstate->cs_ResultTupleSlot; + + commonstate->cs_ProjInfo = projInfo; } /* ---------------- - * ExecFreeProjectionInfo + * ExecFreeProjectionInfo * ---------------- */ void -ExecFreeProjectionInfo(CommonState *commonstate) +ExecFreeProjectionInfo(CommonState * commonstate) { - ProjectionInfo *projInfo; - - /* ---------------- - * get projection info. if NULL then this node has - * none so we just return. - * ---------------- - */ - projInfo = commonstate->cs_ProjInfo; - if (projInfo == NULL) - return; - - /* ---------------- - * clean up memory used. - * ---------------- - */ - if (projInfo->pi_tupValue != NULL) - pfree(projInfo->pi_tupValue); - - pfree(projInfo); - commonstate->cs_ProjInfo = NULL; + ProjectionInfo *projInfo; + + /* ---------------- + * get projection info. if NULL then this node has + * none so we just return. + * ---------------- + */ + projInfo = commonstate->cs_ProjInfo; + if (projInfo == NULL) + return; + + /* ---------------- + * clean up memory used. + * ---------------- + */ + if (projInfo->pi_tupValue != NULL) + pfree(projInfo->pi_tupValue); + + pfree(projInfo); + commonstate->cs_ProjInfo = NULL; } /* ---------------------------------------------------------------- - * the following scan type support functions are for - * those nodes which are stubborn and return tuples in - * their Scan tuple slot instead of their Result tuple - * slot.. luck fur us, these nodes do not do projections - * so we don't have to worry about getting the ProjectionInfo - * right for them... -cim 6/3/91 + * the following scan type support functions are for + * those nodes which are stubborn and return tuples in + * their Scan tuple slot instead of their Result tuple + * slot.. luck fur us, these nodes do not do projections + * so we don't have to worry about getting the ProjectionInfo + * right for them... -cim 6/3/91 * ---------------------------------------------------------------- */ /* ---------------- - * ExecGetScanType + * ExecGetScanType * ---------------- */ TupleDesc -ExecGetScanType(CommonScanState *csstate) +ExecGetScanType(CommonScanState * csstate) { - TupleTableSlot *slot = csstate->css_ScanTupleSlot; - return slot->ttc_tupleDescriptor; + TupleTableSlot *slot = csstate->css_ScanTupleSlot; + + return slot->ttc_tupleDescriptor; } /* ---------------- - * ExecFreeScanType + * ExecFreeScanType * ---------------- */ #ifdef NOT_USED void -ExecFreeScanType(CommonScanState *csstate) +ExecFreeScanType(CommonScanState * csstate) { - TupleTableSlot *slot; - TupleDesc tupType; - - slot = csstate->css_ScanTupleSlot; - tupType = slot->ttc_tupleDescriptor; - -/* ExecFreeTypeInfo(tupType); */ - pfree(tupType); + TupleTableSlot *slot; + TupleDesc tupType; + + slot = csstate->css_ScanTupleSlot; + tupType = slot->ttc_tupleDescriptor; + +/* ExecFreeTypeInfo(tupType); */ + pfree(tupType); } + #endif /* ---------------- - * ExecAssignScanType + * ExecAssignScanType * ---------------- */ void -ExecAssignScanType(CommonScanState *csstate, - TupleDesc tupDesc) +ExecAssignScanType(CommonScanState * csstate, + TupleDesc tupDesc) { - TupleTableSlot *slot; - - slot = (TupleTableSlot *) csstate->css_ScanTupleSlot; - slot->ttc_tupleDescriptor = tupDesc; + TupleTableSlot *slot; + + slot = (TupleTableSlot *) csstate->css_ScanTupleSlot; + slot->ttc_tupleDescriptor = tupDesc; } /* ---------------- - * ExecAssignScanTypeFromOuterPlan + * ExecAssignScanTypeFromOuterPlan * ---------------- */ void -ExecAssignScanTypeFromOuterPlan(Plan *node, CommonScanState *csstate) +ExecAssignScanTypeFromOuterPlan(Plan * node, CommonScanState * csstate) { - Plan *outerPlan; - TupleDesc tupDesc; - - outerPlan = outerPlan(node); - tupDesc = ExecGetTupType(outerPlan); + Plan *outerPlan; + TupleDesc tupDesc; + + outerPlan = outerPlan(node); + tupDesc = ExecGetTupType(outerPlan); - ExecAssignScanType(csstate, tupDesc); + ExecAssignScanType(csstate, tupDesc); } /* ---------------------------------------------------------------- - * ExecTypeFromTL support routines. + * ExecTypeFromTL support routines. * - * these routines are used mainly from ExecTypeFromTL. - * -cim 6/12/90 + * these routines are used mainly from ExecTypeFromTL. + * -cim 6/12/90 * * old comments - * Routines dealing with the structure 'attribute' which conatains - * the type information about attributes in a tuple: + * Routines dealing with the structure 'attribute' which conatains + * the type information about attributes in a tuple: * - * ExecMakeTypeInfo(noType) -- - * returns pointer to array of 'noType' structure 'attribute'. - * ExecSetTypeInfo(index, typeInfo, attNum, attLen) -- - * sets the element indexed by 'index' in typeInfo with - * the values: attNum, attLen. - * ExecFreeTypeInfo(typeInfo) -- - * frees the structure 'typeInfo'. + * ExecMakeTypeInfo(noType) -- + * returns pointer to array of 'noType' structure 'attribute'. + * ExecSetTypeInfo(index, typeInfo, attNum, attLen) -- + * sets the element indexed by 'index' in typeInfo with + * the values: attNum, attLen. + * ExecFreeTypeInfo(typeInfo) -- + * frees the structure 'typeInfo'. * ---------------------------------------------------------------- */ /* ---------------- - * ExecSetTypeInfo + * ExecSetTypeInfo * - * This initializes fields of a single attribute in a - * tuple descriptor from the specified parameters. + * This initializes fields of a single attribute in a + * tuple descriptor from the specified parameters. * - * XXX this duplicates much of the functionality of TupleDescInitEntry. - * the routines should be moved to the same place and be rewritten - * to share common code. + * XXX this duplicates much of the functionality of TupleDescInitEntry. + * the routines should be moved to the same place and be rewritten + * to share common code. * ---------------- */ -#ifdef NOT_USED +#ifdef NOT_USED void ExecSetTypeInfo(int index, - TupleDesc typeInfo, - Oid typeID, - int attNum, - int attLen, - char *attName, - bool attbyVal, - char attalign) + TupleDesc typeInfo, + Oid typeID, + int attNum, + int attLen, + char *attName, + bool attbyVal, + char attalign) { - AttributeTupleForm att; - - /* ---------------- - * get attribute pointer and preform a sanity check.. - * ---------------- - */ - att = typeInfo[index]; - if (att == NULL) - elog(WARN, "ExecSetTypeInfo: trying to assign through NULL"); - - /* ---------------- - * assign values to the tuple descriptor, being careful not - * to copy a null attName.. - * - * XXX it is unknown exactly what information is needed to - * initialize the attribute struct correctly so for now - * we use 0. this should be fixed -- otherwise we run the - * risk of using garbage data. -cim 5/5/91 - * ---------------- - */ - att->attrelid = 0; /* dummy value */ - - if (attName != (char *) NULL) - strNcpy(att->attname.data, attName, NAMEDATALEN-1); - else - memset(att->attname.data,0,NAMEDATALEN); - - att->atttypid = typeID; - att->attdefrel = 0; /* dummy value */ - att->attdisbursion = 0; /* dummy value */ - att->atttyparg = 0; /* dummy value */ - att->attlen = attLen; - att->attnum = attNum; - att->attbound = 0; /* dummy value */ - att->attbyval = attbyVal; - att->attcanindex = 0; /* dummy value */ - att->attproc = 0; /* dummy value */ - att->attnelems = 0; /* dummy value */ - att->attcacheoff = -1; - att->attisset = false; - att->attalign = attalign; + AttributeTupleForm att; + + /* ---------------- + * get attribute pointer and preform a sanity check.. + * ---------------- + */ + att = typeInfo[index]; + if (att == NULL) + elog(WARN, "ExecSetTypeInfo: trying to assign through NULL"); + + /* ---------------- + * assign values to the tuple descriptor, being careful not + * to copy a null attName.. + * + * XXX it is unknown exactly what information is needed to + * initialize the attribute struct correctly so for now + * we use 0. this should be fixed -- otherwise we run the + * risk of using garbage data. -cim 5/5/91 + * ---------------- + */ + att->attrelid = 0; /* dummy value */ + + if (attName != (char *) NULL) + strNcpy(att->attname.data, attName, NAMEDATALEN - 1); + else + memset(att->attname.data, 0, NAMEDATALEN); + + att->atttypid = typeID; + att->attdefrel = 0; /* dummy value */ + att->attdisbursion = 0; /* dummy value */ + att->atttyparg = 0; /* dummy value */ + att->attlen = attLen; + att->attnum = attNum; + att->attbound = 0; /* dummy value */ + att->attbyval = attbyVal; + att->attcanindex = 0; /* dummy value */ + att->attproc = 0; /* dummy value */ + att->attnelems = 0; /* dummy value */ + att->attcacheoff = -1; + att->attisset = false; + att->attalign = attalign; } /* ---------------- - * ExecFreeTypeInfo frees the array of attrbutes - * created by ExecMakeTypeInfo and returned by ExecTypeFromTL... + * ExecFreeTypeInfo frees the array of attrbutes + * created by ExecMakeTypeInfo and returned by ExecTypeFromTL... * ---------------- */ void ExecFreeTypeInfo(TupleDesc typeInfo) { - /* ---------------- - * do nothing if asked to free a null pointer - * ---------------- - */ - if (typeInfo == NULL) - return; - - /* ---------------- - * the entire array of typeinfo pointers created by - * ExecMakeTypeInfo was allocated with a single palloc() - * so we can deallocate the whole array with a single pfree(). - * (we should not try and free all the elements in the array) - * -cim 6/12/90 - * ---------------- - */ - pfree(typeInfo); + /* ---------------- + * do nothing if asked to free a null pointer + * ---------------- + */ + if (typeInfo == NULL) + return; + + /* ---------------- + * the entire array of typeinfo pointers created by + * ExecMakeTypeInfo was allocated with a single palloc() + * so we can deallocate the whole array with a single pfree(). + * (we should not try and free all the elements in the array) + * -cim 6/12/90 + * ---------------- + */ + pfree(typeInfo); } /* ---------------------------------------------------------------- - * QueryDescGetTypeInfo + * QueryDescGetTypeInfo * - *| I don't know how this is used, all I know is that it - *| appeared one day in main.c so I moved it here. -cim 11/1/89 + *| I don't know how this is used, all I know is that it + *| appeared one day in main.c so I moved it here. -cim 11/1/89 * ---------------------------------------------------------------- */ TupleDesc -QueryDescGetTypeInfo(QueryDesc *queryDesc) +QueryDescGetTypeInfo(QueryDesc * queryDesc) { - Plan *plan; - TupleDesc tupleType; - List *targetList; - AttrInfo *attinfo = (AttrInfo *)palloc(sizeof(AttrInfo)); - - plan = queryDesc->plantree; - tupleType = (TupleDesc) ExecGetTupType(plan); + Plan *plan; + TupleDesc tupleType; + List *targetList; + AttrInfo *attinfo = (AttrInfo *) palloc(sizeof(AttrInfo)); + + plan = queryDesc->plantree; + tupleType = (TupleDesc) ExecGetTupType(plan); /* - targetList = plan->targetlist; + targetList = plan->targetlist; - attinfo->numAttr = ExecTargetListLength(targetList); - attinfo->attrs = tupleType; + attinfo->numAttr = ExecTargetListLength(targetList); + attinfo->attrs = tupleType; */ - attinfo->numAttr = tupleType->natts; - attinfo->attrs = tupleType->attrs; - return attinfo; + attinfo->numAttr = tupleType->natts; + attinfo->attrs = tupleType->attrs; + return attinfo; } + #endif /* ---------------------------------------------------------------- - * ExecInsertIndexTuples support + * ExecInsertIndexTuples support * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- - * ExecGetIndexKeyInfo + * ExecGetIndexKeyInfo * - * Extracts the index key attribute numbers from - * an index tuple form (i.e. a tuple from the pg_index relation) - * into an array of attribute numbers. The array and the - * size of the array are returned to the caller via return - * parameters. + * Extracts the index key attribute numbers from + * an index tuple form (i.e. a tuple from the pg_index relation) + * into an array of attribute numbers. The array and the + * size of the array are returned to the caller via return + * parameters. * ---------------------------------------------------------------- */ static void ExecGetIndexKeyInfo(IndexTupleForm indexTuple, - int *numAttsOutP, - AttrNumber **attsOutP, - FuncIndexInfoPtr fInfoP) + int *numAttsOutP, + AttrNumber ** attsOutP, + FuncIndexInfoPtr fInfoP) { - int i; - int numKeys; - AttrNumber *attKeys; - - /* ---------------- - * check parameters - * ---------------- - */ - if (numAttsOutP == NULL && attsOutP == NULL) { - elog(DEBUG, "ExecGetIndexKeyInfo: %s", - "invalid parameters: numAttsOutP and attsOutP must be non-NULL"); - } - - /* ---------------- - * set the procid for a possible functional index. - * ---------------- - */ - FIsetProcOid(fInfoP, indexTuple->indproc); - - /* ---------------- - * count the number of keys.. - * ---------------- - */ - numKeys = 0; - for (i=0; i<8 && indexTuple->indkey[i] != 0; i++) - numKeys++; - - /* ---------------- - * place number keys in callers return area - * or the number of arguments for a functional index. - * - * If we have a functional index then the number of - * attributes defined in the index must 1 (the function's - * single return value). - * ---------------- - */ - if (FIgetProcOid(fInfoP) != InvalidOid) { - FIsetnArgs(fInfoP, numKeys); - (*numAttsOutP) = 1; - } - else - (*numAttsOutP) = numKeys; - - if (numKeys < 1) { - elog(DEBUG, "ExecGetIndexKeyInfo: %s", - "all index key attribute numbers are zero!"); - (*attsOutP) = NULL; - return; - } - - /* ---------------- - * allocate and fill in array of key attribute numbers - * ---------------- - */ - CXT1_printf("ExecGetIndexKeyInfo: context is %d\n", CurrentMemoryContext); - - attKeys = (AttrNumber*) - palloc(numKeys * sizeof(AttrNumber)); - - for (i=0; i<numKeys; i++) - attKeys[i] = indexTuple->indkey[i]; - - /* ---------------- - * return array to caller. - * ---------------- - */ - (*attsOutP) = attKeys; -} + int i; + int numKeys; + AttrNumber *attKeys; -/* ---------------------------------------------------------------- - * ExecOpenIndices - * - * Here we scan the pg_index relation to find indices - * associated with a given heap relation oid. Since we - * don't know in advance how many indices we have, we - * form lists containing the information we need from - * pg_index and then process these lists. - * - * Note: much of this code duplicates effort done by - * the IndexCatalogInformation function in plancat.c - * because IndexCatalogInformation is poorly written. - * - * It would be much better the functionality provided - * by this function and IndexCatalogInformation was - * in the form of a small set of orthogonal routines.. - * If you are trying to understand this, I suggest you - * look at the code to IndexCatalogInformation and - * FormIndexTuple.. -cim 9/27/89 - * ---------------------------------------------------------------- - */ -void -ExecOpenIndices(Oid resultRelationOid, - RelationInfo *resultRelationInfo) -{ - Relation indexRd; - HeapScanDesc indexSd; - ScanKeyData key; - HeapTuple tuple; - IndexTupleForm indexStruct; - Oid indexOid; - List *oidList; - List *nkeyList; - List *keyList; - List *fiList; - char *predString; - List *predList; - List *indexoid; - List *numkeys; - List *indexkeys; - List *indexfuncs; - List *indexpreds; - int len; - - RelationPtr relationDescs; - IndexInfo **indexInfoArray; - FuncIndexInfoPtr fInfoP; - int numKeyAtts; - AttrNumber *indexKeyAtts; - PredInfo *predicate; - int i; - - /* ---------------- - * open pg_index - * ---------------- - */ - indexRd = heap_openr(IndexRelationName); - - /* ---------------- - * form a scan key - * ---------------- - */ - ScanKeyEntryInitialize(&key, 0, Anum_pg_index_indrelid, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(resultRelationOid)); - - /* ---------------- - * scan the index relation, looking for indices for our - * result relation.. - * ---------------- - */ - indexSd = heap_beginscan(indexRd, /* scan desc */ - false, /* scan backward flag */ - NowTimeQual, /* time qual */ - 1, /* number scan keys */ - &key); /* scan keys */ - - oidList = NIL; - nkeyList = NIL; - keyList = NIL; - fiList = NIL; - predList = NIL; - - while(tuple = heap_getnext(indexSd, /* scan desc */ - false, /* scan backward flag */ - NULL), /* return: buffer */ - HeapTupleIsValid(tuple)) { - /* ---------------- - * For each index relation we find, extract the information - * we need and store it in a list.. - * - * first get the oid of the index relation from the tuple + * check parameters * ---------------- */ - indexStruct = (IndexTupleForm) GETSTRUCT(tuple); - indexOid = indexStruct->indexrelid; - + if (numAttsOutP == NULL && attsOutP == NULL) + { + elog(DEBUG, "ExecGetIndexKeyInfo: %s", + "invalid parameters: numAttsOutP and attsOutP must be non-NULL"); + } + /* ---------------- - * allocate space for functional index information. + * set the procid for a possible functional index. * ---------------- */ - fInfoP = (FuncIndexInfoPtr)palloc( sizeof(*fInfoP) ); - + FIsetProcOid(fInfoP, indexTuple->indproc); + /* ---------------- - * next get the index key information from the tuple + * count the number of keys.. * ---------------- */ - ExecGetIndexKeyInfo(indexStruct, - &numKeyAtts, - &indexKeyAtts, - fInfoP); - + numKeys = 0; + for (i = 0; i < 8 && indexTuple->indkey[i] != 0; i++) + numKeys++; + /* ---------------- - * next get the index predicate from the tuple + * place number keys in callers return area + * or the number of arguments for a functional index. + * + * If we have a functional index then the number of + * attributes defined in the index must 1 (the function's + * single return value). * ---------------- */ - if (VARSIZE(&indexStruct->indpred) != 0) { - predString = fmgr(F_TEXTOUT, &indexStruct->indpred); - predicate = (PredInfo*)stringToNode(predString); - pfree(predString); - } else { - predicate = NULL; + if (FIgetProcOid(fInfoP) != InvalidOid) + { + FIsetnArgs(fInfoP, numKeys); + (*numAttsOutP) = 1; + } + else + (*numAttsOutP) = numKeys; + + if (numKeys < 1) + { + elog(DEBUG, "ExecGetIndexKeyInfo: %s", + "all index key attribute numbers are zero!"); + (*attsOutP) = NULL; + return; } - + /* ---------------- - * save the index information into lists + * allocate and fill in array of key attribute numbers * ---------------- */ - oidList = lconsi(indexOid, oidList); - nkeyList = lconsi(numKeyAtts, nkeyList); - keyList = lcons(indexKeyAtts, keyList); - fiList = lcons(fInfoP, fiList); - predList = lcons(predicate, predList); - } - - /* ---------------- - * we have the info we need so close the pg_index relation.. - * ---------------- - */ - heap_endscan(indexSd); - heap_close(indexRd); - - /* ---------------- - * Now that we've collected the index information into three - * lists, we open the index relations and store the descriptors - * and the key information into arrays. - * ---------------- - */ - len = length(oidList); - if (len > 0) { + CXT1_printf("ExecGetIndexKeyInfo: context is %d\n", CurrentMemoryContext); + + attKeys = (AttrNumber *) + palloc(numKeys * sizeof(AttrNumber)); + + for (i = 0; i < numKeys; i++) + attKeys[i] = indexTuple->indkey[i]; + /* ---------------- - * allocate space for relation descs + * return array to caller. * ---------------- */ - CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext); - relationDescs = (RelationPtr) - palloc(len * sizeof(Relation)); - + (*attsOutP) = attKeys; +} + +/* ---------------------------------------------------------------- + * ExecOpenIndices + * + * Here we scan the pg_index relation to find indices + * associated with a given heap relation oid. Since we + * don't know in advance how many indices we have, we + * form lists containing the information we need from + * pg_index and then process these lists. + * + * Note: much of this code duplicates effort done by + * the IndexCatalogInformation function in plancat.c + * because IndexCatalogInformation is poorly written. + * + * It would be much better the functionality provided + * by this function and IndexCatalogInformation was + * in the form of a small set of orthogonal routines.. + * If you are trying to understand this, I suggest you + * look at the code to IndexCatalogInformation and + * FormIndexTuple.. -cim 9/27/89 + * ---------------------------------------------------------------- + */ +void +ExecOpenIndices(Oid resultRelationOid, + RelationInfo * resultRelationInfo) +{ + Relation indexRd; + HeapScanDesc indexSd; + ScanKeyData key; + HeapTuple tuple; + IndexTupleForm indexStruct; + Oid indexOid; + List *oidList; + List *nkeyList; + List *keyList; + List *fiList; + char *predString; + List *predList; + List *indexoid; + List *numkeys; + List *indexkeys; + List *indexfuncs; + List *indexpreds; + int len; + + RelationPtr relationDescs; + IndexInfo **indexInfoArray; + FuncIndexInfoPtr fInfoP; + int numKeyAtts; + AttrNumber *indexKeyAtts; + PredInfo *predicate; + int i; + /* ---------------- - * initialize index info array + * open pg_index * ---------------- */ - CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext); - indexInfoArray = (IndexInfo**) - palloc(len * sizeof(IndexInfo*)); - - for (i=0; i<len; i++) { - IndexInfo *ii = makeNode(IndexInfo); - ii->ii_NumKeyAttributes = 0; - ii->ii_KeyAttributeNumbers = (AttrNumber*) NULL; - ii->ii_FuncIndexInfo = (FuncIndexInfoPtr) NULL; - ii->ii_Predicate = NULL; - indexInfoArray[i] = ii; - } - + indexRd = heap_openr(IndexRelationName); + /* ---------------- - * attempt to open each of the indices. If we succeed, - * then store the index relation descriptor into the - * relation descriptor array. + * form a scan key * ---------------- */ - i = 0; - foreach (indexoid, oidList) { - Relation indexDesc; - - indexOid = lfirsti(indexoid); - indexDesc = index_open(indexOid); - if (indexDesc != NULL) - relationDescs[i++] = indexDesc; + ScanKeyEntryInitialize(&key, 0, Anum_pg_index_indrelid, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(resultRelationOid)); + + /* ---------------- + * scan the index relation, looking for indices for our + * result relation.. + * ---------------- + */ + indexSd = heap_beginscan(indexRd, /* scan desc */ + false, /* scan backward flag */ + NowTimeQual, /* time qual */ + 1, /* number scan keys */ + &key); /* scan keys */ + + oidList = NIL; + nkeyList = NIL; + keyList = NIL; + fiList = NIL; + predList = NIL; + + while (tuple = heap_getnext(indexSd, /* scan desc */ + false, /* scan backward flag */ + NULL), /* return: buffer */ + HeapTupleIsValid(tuple)) + { + + /* ---------------- + * For each index relation we find, extract the information + * we need and store it in a list.. + * + * first get the oid of the index relation from the tuple + * ---------------- + */ + indexStruct = (IndexTupleForm) GETSTRUCT(tuple); + indexOid = indexStruct->indexrelid; + + /* ---------------- + * allocate space for functional index information. + * ---------------- + */ + fInfoP = (FuncIndexInfoPtr) palloc(sizeof(*fInfoP)); + + /* ---------------- + * next get the index key information from the tuple + * ---------------- + */ + ExecGetIndexKeyInfo(indexStruct, + &numKeyAtts, + &indexKeyAtts, + fInfoP); + + /* ---------------- + * next get the index predicate from the tuple + * ---------------- + */ + if (VARSIZE(&indexStruct->indpred) != 0) + { + predString = fmgr(F_TEXTOUT, &indexStruct->indpred); + predicate = (PredInfo *) stringToNode(predString); + pfree(predString); + } + else + { + predicate = NULL; + } + + /* ---------------- + * save the index information into lists + * ---------------- + */ + oidList = lconsi(indexOid, oidList); + nkeyList = lconsi(numKeyAtts, nkeyList); + keyList = lcons(indexKeyAtts, keyList); + fiList = lcons(fInfoP, fiList); + predList = lcons(predicate, predList); } - + /* ---------------- - * store the relation descriptor array and number of - * descs into the result relation info. + * we have the info we need so close the pg_index relation.. * ---------------- */ - resultRelationInfo->ri_NumIndices = i; - resultRelationInfo->ri_IndexRelationDescs = relationDescs; - + heap_endscan(indexSd); + heap_close(indexRd); + /* ---------------- - * store the index key information collected in our - * lists into the index info array + * Now that we've collected the index information into three + * lists, we open the index relations and store the descriptors + * and the key information into arrays. * ---------------- */ - i = 0; - foreach (numkeys, nkeyList) { - numKeyAtts = lfirsti(numkeys); - indexInfoArray[i++]->ii_NumKeyAttributes = numKeyAtts; - } - - i = 0; - foreach (indexkeys, keyList) { - indexKeyAtts = (AttrNumber*) lfirst(indexkeys); - indexInfoArray[i++]->ii_KeyAttributeNumbers = indexKeyAtts; - } - - i = 0; - foreach (indexfuncs, fiList) { - FuncIndexInfoPtr fiP = (FuncIndexInfoPtr)lfirst(indexfuncs); - indexInfoArray[i++]->ii_FuncIndexInfo = fiP; - } - - i = 0; - foreach (indexpreds, predList) { - indexInfoArray[i++]->ii_Predicate = lfirst(indexpreds); + len = length(oidList); + if (len > 0) + { + /* ---------------- + * allocate space for relation descs + * ---------------- + */ + CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext); + relationDescs = (RelationPtr) + palloc(len * sizeof(Relation)); + + /* ---------------- + * initialize index info array + * ---------------- + */ + CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext); + indexInfoArray = (IndexInfo **) + palloc(len * sizeof(IndexInfo *)); + + for (i = 0; i < len; i++) + { + IndexInfo *ii = makeNode(IndexInfo); + + ii->ii_NumKeyAttributes = 0; + ii->ii_KeyAttributeNumbers = (AttrNumber *) NULL; + ii->ii_FuncIndexInfo = (FuncIndexInfoPtr) NULL; + ii->ii_Predicate = NULL; + indexInfoArray[i] = ii; + } + + /* ---------------- + * attempt to open each of the indices. If we succeed, + * then store the index relation descriptor into the + * relation descriptor array. + * ---------------- + */ + i = 0; + foreach(indexoid, oidList) + { + Relation indexDesc; + + indexOid = lfirsti(indexoid); + indexDesc = index_open(indexOid); + if (indexDesc != NULL) + relationDescs[i++] = indexDesc; + } + + /* ---------------- + * store the relation descriptor array and number of + * descs into the result relation info. + * ---------------- + */ + resultRelationInfo->ri_NumIndices = i; + resultRelationInfo->ri_IndexRelationDescs = relationDescs; + + /* ---------------- + * store the index key information collected in our + * lists into the index info array + * ---------------- + */ + i = 0; + foreach(numkeys, nkeyList) + { + numKeyAtts = lfirsti(numkeys); + indexInfoArray[i++]->ii_NumKeyAttributes = numKeyAtts; + } + + i = 0; + foreach(indexkeys, keyList) + { + indexKeyAtts = (AttrNumber *) lfirst(indexkeys); + indexInfoArray[i++]->ii_KeyAttributeNumbers = indexKeyAtts; + } + + i = 0; + foreach(indexfuncs, fiList) + { + FuncIndexInfoPtr fiP = (FuncIndexInfoPtr) lfirst(indexfuncs); + + indexInfoArray[i++]->ii_FuncIndexInfo = fiP; + } + + i = 0; + foreach(indexpreds, predList) + { + indexInfoArray[i++]->ii_Predicate = lfirst(indexpreds); + } + /* ---------------- + * store the index info array into relation info + * ---------------- + */ + resultRelationInfo->ri_IndexRelationInfo = indexInfoArray; } + /* ---------------- - * store the index info array into relation info + * All done, resultRelationInfo now contains complete information + * on the indices associated with the result relation. * ---------------- */ - resultRelationInfo->ri_IndexRelationInfo = indexInfoArray; - } - - /* ---------------- - * All done, resultRelationInfo now contains complete information - * on the indices associated with the result relation. - * ---------------- - */ - - /* should free oidList, nkeyList and keyList here */ - /* OK - let's do it -jolly */ - freeList(oidList); - freeList(nkeyList); - freeList(keyList); - freeList(fiList); - freeList(predList); + + /* should free oidList, nkeyList and keyList here */ + /* OK - let's do it -jolly */ + freeList(oidList); + freeList(nkeyList); + freeList(keyList); + freeList(fiList); + freeList(predList); } /* ---------------------------------------------------------------- - * ExecCloseIndices + * ExecCloseIndices * - * Close the index relations stored in resultRelationInfo + * Close the index relations stored in resultRelationInfo * ---------------------------------------------------------------- */ void -ExecCloseIndices(RelationInfo *resultRelationInfo) +ExecCloseIndices(RelationInfo * resultRelationInfo) { - int i; - int numIndices; - RelationPtr relationDescs; - - numIndices = resultRelationInfo->ri_NumIndices; - relationDescs = resultRelationInfo->ri_IndexRelationDescs; - - for (i=0; i<numIndices; i++) - if (relationDescs[i] != NULL) - index_close(relationDescs[i]); - /* - * XXX should free indexInfo array here too. - */ + int i; + int numIndices; + RelationPtr relationDescs; + + numIndices = resultRelationInfo->ri_NumIndices; + relationDescs = resultRelationInfo->ri_IndexRelationDescs; + + for (i = 0; i < numIndices; i++) + if (relationDescs[i] != NULL) + index_close(relationDescs[i]); + + /* + * XXX should free indexInfo array here too. + */ } /* ---------------------------------------------------------------- - * ExecFormIndexTuple + * ExecFormIndexTuple * - * Most of this code is cannabilized from DefaultBuild(). - * As said in the comments for ExecOpenIndices, most of - * this functionality should be rearranged into a proper - * set of routines.. + * Most of this code is cannabilized from DefaultBuild(). + * As said in the comments for ExecOpenIndices, most of + * this functionality should be rearranged into a proper + * set of routines.. * ---------------------------------------------------------------- */ #ifdef NOT_USED IndexTuple ExecFormIndexTuple(HeapTuple heapTuple, - Relation heapRelation, - Relation indexRelation, - IndexInfo *indexInfo) + Relation heapRelation, + Relation indexRelation, + IndexInfo * indexInfo) { - IndexTuple indexTuple; - TupleDesc heapDescriptor; - TupleDesc indexDescriptor; - Datum *datum; - char *nulls; - - int numberOfAttributes; - AttrNumber *keyAttributeNumbers; - FuncIndexInfoPtr fInfoP; - - /* ---------------- - * get information from index info structure - * ---------------- - */ - numberOfAttributes = indexInfo->ii_NumKeyAttributes; - keyAttributeNumbers = indexInfo->ii_KeyAttributeNumbers; - fInfoP = indexInfo->ii_FuncIndexInfo; - - /* ---------------- - * datum and null are arrays in which we collect the index attributes - * when forming a new index tuple. - * ---------------- - */ - CXT1_printf("ExecFormIndexTuple: context is %d\n", CurrentMemoryContext); - datum = (Datum *) palloc(numberOfAttributes * sizeof *datum); - nulls = (char *) palloc(numberOfAttributes * sizeof *nulls); - - /* ---------------- - * get the tuple descriptors from the relations so we know - * how to form the index tuples.. - * ---------------- - */ - heapDescriptor = RelationGetTupleDescriptor(heapRelation); - indexDescriptor = RelationGetTupleDescriptor(indexRelation); - - /* ---------------- - * FormIndexDatum fills in its datum and null parameters - * with attribute information taken from the given heap tuple. - * ---------------- - */ - FormIndexDatum(numberOfAttributes, /* num attributes */ - keyAttributeNumbers, /* array of att nums to extract */ - heapTuple, /* tuple from base relation */ - heapDescriptor, /* heap tuple's descriptor */ - InvalidBuffer, /* buffer associated with heap tuple */ - datum, /* return: array of attributes */ - nulls, /* return: array of char's */ - fInfoP); /* functional index information */ - - indexTuple = index_formtuple(indexDescriptor, - datum, - nulls); - - /* ---------------- - * free temporary arrays - * - * XXX should store these in the IndexInfo instead of allocating - * and freeing on every insertion, but efficency here is not - * that important and FormIndexTuple is wasteful anyways.. - * -cim 9/27/89 - * ---------------- - */ - pfree(nulls); - pfree(datum); - - return indexTuple; + IndexTuple indexTuple; + TupleDesc heapDescriptor; + TupleDesc indexDescriptor; + Datum *datum; + char *nulls; + + int numberOfAttributes; + AttrNumber *keyAttributeNumbers; + FuncIndexInfoPtr fInfoP; + + /* ---------------- + * get information from index info structure + * ---------------- + */ + numberOfAttributes = indexInfo->ii_NumKeyAttributes; + keyAttributeNumbers = indexInfo->ii_KeyAttributeNumbers; + fInfoP = indexInfo->ii_FuncIndexInfo; + + /* ---------------- + * datum and null are arrays in which we collect the index attributes + * when forming a new index tuple. + * ---------------- + */ + CXT1_printf("ExecFormIndexTuple: context is %d\n", CurrentMemoryContext); + datum = (Datum *) palloc(numberOfAttributes * sizeof *datum); + nulls = (char *) palloc(numberOfAttributes * sizeof *nulls); + + /* ---------------- + * get the tuple descriptors from the relations so we know + * how to form the index tuples.. + * ---------------- + */ + heapDescriptor = RelationGetTupleDescriptor(heapRelation); + indexDescriptor = RelationGetTupleDescriptor(indexRelation); + + /* ---------------- + * FormIndexDatum fills in its datum and null parameters + * with attribute information taken from the given heap tuple. + * ---------------- + */ + FormIndexDatum(numberOfAttributes, /* num attributes */ + keyAttributeNumbers, /* array of att nums to extract */ + heapTuple, /* tuple from base relation */ + heapDescriptor, /* heap tuple's descriptor */ + InvalidBuffer, /* buffer associated with heap + * tuple */ + datum, /* return: array of attributes */ + nulls, /* return: array of char's */ + fInfoP); /* functional index information */ + + indexTuple = index_formtuple(indexDescriptor, + datum, + nulls); + + /* ---------------- + * free temporary arrays + * + * XXX should store these in the IndexInfo instead of allocating + * and freeing on every insertion, but efficency here is not + * that important and FormIndexTuple is wasteful anyways.. + * -cim 9/27/89 + * ---------------- + */ + pfree(nulls); + pfree(datum); + + return indexTuple; } + #endif /* ---------------------------------------------------------------- - * ExecInsertIndexTuples + * ExecInsertIndexTuples * - * This routine takes care of inserting index tuples - * into all the relations indexing the result relation - * when a heap tuple is inserted into the result relation. - * Much of this code should be moved into the genam - * stuff as it only exists here because the genam stuff - * doesn't provide the functionality needed by the - * executor.. -cim 9/27/89 + * This routine takes care of inserting index tuples + * into all the relations indexing the result relation + * when a heap tuple is inserted into the result relation. + * Much of this code should be moved into the genam + * stuff as it only exists here because the genam stuff + * doesn't provide the functionality needed by the + * executor.. -cim 9/27/89 * ---------------------------------------------------------------- */ void -ExecInsertIndexTuples(TupleTableSlot *slot, - ItemPointer tupleid, - EState *estate, - bool is_update) +ExecInsertIndexTuples(TupleTableSlot * slot, + ItemPointer tupleid, + EState * estate, + bool is_update) { - HeapTuple heapTuple; - RelationInfo *resultRelationInfo; - int i; - int numIndices; - RelationPtr relationDescs; - Relation heapRelation; - IndexInfo **indexInfoArray; - IndexInfo *indexInfo; - Node *predicate; - bool satisfied; - ExprContext *econtext; - InsertIndexResult result; - int numberOfAttributes; - AttrNumber *keyAttributeNumbers; - FuncIndexInfoPtr fInfoP; - TupleDesc heapDescriptor; - Datum *datum; - char *nulls; - - heapTuple = slot->val; - - /* ---------------- - * get information from the result relation info structure. - * ---------------- - */ - resultRelationInfo = estate->es_result_relation_info; - numIndices = resultRelationInfo->ri_NumIndices; - relationDescs = resultRelationInfo->ri_IndexRelationDescs; - indexInfoArray = resultRelationInfo->ri_IndexRelationInfo; - heapRelation = resultRelationInfo->ri_RelationDesc; - - /* ---------------- - * for each index, form and insert the index tuple - * ---------------- - */ - econtext = NULL; - for (i=0; i<numIndices; i++) { - if (relationDescs[i] == NULL) continue; - - indexInfo = indexInfoArray[i]; - predicate = indexInfo->ii_Predicate; - if (predicate != NULL) { - if (econtext == NULL) { - econtext = makeNode(ExprContext); - } - econtext->ecxt_scantuple = slot; - - /* Skip this index-update if the predicate isn't satisfied */ - satisfied = ExecQual((List*)predicate, econtext); - if (satisfied == false) - continue; - } + HeapTuple heapTuple; + RelationInfo *resultRelationInfo; + int i; + int numIndices; + RelationPtr relationDescs; + Relation heapRelation; + IndexInfo **indexInfoArray; + IndexInfo *indexInfo; + Node *predicate; + bool satisfied; + ExprContext *econtext; + InsertIndexResult result; + int numberOfAttributes; + AttrNumber *keyAttributeNumbers; + FuncIndexInfoPtr fInfoP; + TupleDesc heapDescriptor; + Datum *datum; + char *nulls; + + heapTuple = slot->val; /* ---------------- - * get information from index info structure + * get information from the result relation info structure. * ---------------- */ - numberOfAttributes = indexInfo->ii_NumKeyAttributes; - keyAttributeNumbers = indexInfo->ii_KeyAttributeNumbers; - fInfoP = indexInfo->ii_FuncIndexInfo; - datum = (Datum *) palloc(numberOfAttributes * sizeof *datum); - nulls = (char *) palloc(numberOfAttributes * sizeof *nulls); - heapDescriptor = (TupleDesc)RelationGetTupleDescriptor(heapRelation); - - FormIndexDatum(numberOfAttributes, /* num attributes */ - keyAttributeNumbers, /* array of att nums to extract */ - heapTuple, /* tuple from base relation */ - heapDescriptor, /* heap tuple's descriptor */ - InvalidBuffer, /* buffer associated with heap tuple */ - datum, /* return: array of attributes */ - nulls, /* return: array of char's */ - fInfoP); /* functional index information */ - - - result = index_insert(relationDescs[i], /* index relation */ - datum, /* array of heaptuple Datums */ - nulls, /* info on nulls */ - &(heapTuple->t_ctid), /* oid of heap tuple */ - heapRelation); - - /* ---------------- - * keep track of index inserts for debugging - * ---------------- - */ - IncrIndexInserted(); - + resultRelationInfo = estate->es_result_relation_info; + numIndices = resultRelationInfo->ri_NumIndices; + relationDescs = resultRelationInfo->ri_IndexRelationDescs; + indexInfoArray = resultRelationInfo->ri_IndexRelationInfo; + heapRelation = resultRelationInfo->ri_RelationDesc; + /* ---------------- - * free index tuple after insertion + * for each index, form and insert the index tuple * ---------------- */ - if (result) pfree(result); - } - if (econtext != NULL) pfree(econtext); + econtext = NULL; + for (i = 0; i < numIndices; i++) + { + if (relationDescs[i] == NULL) + continue; + + indexInfo = indexInfoArray[i]; + predicate = indexInfo->ii_Predicate; + if (predicate != NULL) + { + if (econtext == NULL) + { + econtext = makeNode(ExprContext); + } + econtext->ecxt_scantuple = slot; + + /* Skip this index-update if the predicate isn't satisfied */ + satisfied = ExecQual((List *) predicate, econtext); + if (satisfied == false) + continue; + } + + /* ---------------- + * get information from index info structure + * ---------------- + */ + numberOfAttributes = indexInfo->ii_NumKeyAttributes; + keyAttributeNumbers = indexInfo->ii_KeyAttributeNumbers; + fInfoP = indexInfo->ii_FuncIndexInfo; + datum = (Datum *) palloc(numberOfAttributes * sizeof *datum); + nulls = (char *) palloc(numberOfAttributes * sizeof *nulls); + heapDescriptor = (TupleDesc) RelationGetTupleDescriptor(heapRelation); + + FormIndexDatum(numberOfAttributes, /* num attributes */ + keyAttributeNumbers, /* array of att nums to + * extract */ + heapTuple, /* tuple from base relation */ + heapDescriptor, /* heap tuple's descriptor */ + InvalidBuffer, /* buffer associated with heap + * tuple */ + datum, /* return: array of attributes */ + nulls, /* return: array of char's */ + fInfoP); /* functional index information */ + + + result = index_insert(relationDescs[i], /* index relation */ + datum, /* array of heaptuple Datums */ + nulls, /* info on nulls */ + &(heapTuple->t_ctid), /* oid of heap tuple */ + heapRelation); + + /* ---------------- + * keep track of index inserts for debugging + * ---------------- + */ + IncrIndexInserted(); + + /* ---------------- + * free index tuple after insertion + * ---------------- + */ + if (result) + pfree(result); + } + if (econtext != NULL) + pfree(econtext); } /* ---------------------------------------------------------------- * setVarAttrLenForCreateTable - - * called when we do a SELECT * INTO TABLE tab - * needed for attributes that have a defined length, like bpchar and - * varchar + * called when we do a SELECT * INTO TABLE tab + * needed for attributes that have a defined length, like bpchar and + * varchar * ---------------------------------------------------------------- */ void -setVarAttrLenForCreateTable(TupleDesc tupType, List *targetList, - List *rangeTable) +setVarAttrLenForCreateTable(TupleDesc tupType, List * targetList, + List * rangeTable) { - List *tl; - TargetEntry *tle; - Node *expr; - int varno; - - tl = targetList; - - for (varno = 0; varno < tupType->natts; varno++) { - tle = lfirst(tl); - - if (tupType->attrs[varno]->atttypid == BPCHAROID || - tupType->attrs[varno]->atttypid == VARCHAROID) { - expr = tle->expr; - if (expr && IsA(expr,Var)) { - Var *var; - RangeTblEntry *rtentry; - Relation rd; - - var = (Var *)expr; - rtentry = rt_fetch(var->varnoold, rangeTable); - rd = heap_open(rtentry->relid); - /* set length to that defined in relation */ - tupType->attrs[varno]->attlen = - (*rd->rd_att->attrs[var->varoattno-1]).attlen; - heap_close(rd); - } - else - elog(WARN, "setVarAttrLenForCreateTable: can't get length for variable-length field"); - } - tl = lnext(tl); - } + List *tl; + TargetEntry *tle; + Node *expr; + int varno; + + tl = targetList; + + for (varno = 0; varno < tupType->natts; varno++) + { + tle = lfirst(tl); + + if (tupType->attrs[varno]->atttypid == BPCHAROID || + tupType->attrs[varno]->atttypid == VARCHAROID) + { + expr = tle->expr; + if (expr && IsA(expr, Var)) + { + Var *var; + RangeTblEntry *rtentry; + Relation rd; + + var = (Var *) expr; + rtentry = rt_fetch(var->varnoold, rangeTable); + rd = heap_open(rtentry->relid); + /* set length to that defined in relation */ + tupType->attrs[varno]->attlen = + (*rd->rd_att->attrs[var->varoattno - 1]).attlen; + heap_close(rd); + } + else + elog(WARN, "setVarAttrLenForCreateTable: can't get length for variable-length field"); + } + tl = lnext(tl); + } } -#ifdef NOT_USED /* look at execMain.c */ +#ifdef NOT_USED /* look at execMain.c */ /* ---------------------------------------------------------------- * resetVarAttrLenForCreateTable - - * called when we do a SELECT * INTO TABLE tab - * needed for attributes that have a defined length, like bpchar and - * varchar - * resets length to -1 for those types + * called when we do a SELECT * INTO TABLE tab + * needed for attributes that have a defined length, like bpchar and + * varchar + * resets length to -1 for those types * ---------------------------------------------------------------- */ void resetVarAttrLenForCreateTable(TupleDesc tupType) { - int varno; - - for (varno = 0; varno < tupType->natts; varno++) { - if (tupType->attrs[varno]->atttypid == BPCHAROID || - tupType->attrs[varno]->atttypid == VARCHAROID) - /* set length to original -1 */ - tupType->attrs[varno]->attlen = -1; - } + int varno; + + for (varno = 0; varno < tupType->natts; varno++) + { + if (tupType->attrs[varno]->atttypid == BPCHAROID || + tupType->attrs[varno]->atttypid == VARCHAROID) + /* set length to original -1 */ + tupType->attrs[varno]->attlen = -1; + } } + #endif diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 79f8bede085..96b9b19dcb6 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -1,14 +1,14 @@ /*------------------------------------------------------------------------- * * functions.c-- - * Routines to handle functions called from the executor - * Putting this stuff in fmgr makes the postmaster a mess.... + * Routines to handle functions called from the executor + * Putting this stuff in fmgr makes the postmaster a mess.... * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.7 1997/08/29 09:02:50 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.8 1997/09/07 04:41:27 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -41,401 +41,438 @@ #undef new -typedef enum {F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE} ExecStatus; +typedef enum +{ + F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE +} ExecStatus; -typedef struct local_es { - QueryDesc *qd; - EState *estate; - struct local_es *next; - ExecStatus status; -} execution_state; +typedef struct local_es +{ + QueryDesc *qd; + EState *estate; + struct local_es *next; + ExecStatus status; +} execution_state; #define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *)NULL) /* non-export function prototypes */ -static TupleDesc postquel_start(execution_state *es); -static execution_state *init_execution_state(FunctionCachePtr fcache, - char *args[]); -static TupleTableSlot *postquel_getnext(execution_state *es); -static void postquel_end(execution_state *es); -static void postquel_sub_params(execution_state *es, int nargs, - char *args[], bool *nullV); -static Datum postquel_execute(execution_state *es, FunctionCachePtr fcache, - List *fTlist, char **args, bool *isNull); - +static TupleDesc postquel_start(execution_state * es); +static execution_state * +init_execution_state(FunctionCachePtr fcache, + char *args[]); +static TupleTableSlot *postquel_getnext(execution_state * es); +static void postquel_end(execution_state * es); +static void +postquel_sub_params(execution_state * es, int nargs, + char *args[], bool * nullV); +static Datum +postquel_execute(execution_state * es, FunctionCachePtr fcache, + List * fTlist, char **args, bool * isNull); + Datum ProjectAttribute(TupleDesc TD, - TargetEntry *tlist, - HeapTuple tup, - bool *isnullP) + TargetEntry * tlist, + HeapTuple tup, + bool * isnullP) { - Datum val,valueP; - Var *attrVar = (Var *)tlist->expr; - AttrNumber attrno = attrVar->varattno; - - - val = PointerGetDatum(heap_getattr(tup, - InvalidBuffer, - attrno, - TD, - isnullP)); - if (*isnullP) - return (Datum) NULL; - - valueP = datumCopy(val, - TD->attrs[attrno-1]->atttypid, - TD->attrs[attrno-1]->attbyval, - (Size) TD->attrs[attrno-1]->attlen); - return valueP; + Datum val, + valueP; + Var *attrVar = (Var *) tlist->expr; + AttrNumber attrno = attrVar->varattno; + + + val = PointerGetDatum(heap_getattr(tup, + InvalidBuffer, + attrno, + TD, + isnullP)); + if (*isnullP) + return (Datum) NULL; + + valueP = datumCopy(val, + TD->attrs[attrno - 1]->atttypid, + TD->attrs[attrno - 1]->attbyval, + (Size) TD->attrs[attrno - 1]->attlen); + return valueP; } static execution_state * init_execution_state(FunctionCachePtr fcache, - char *args[]) + char *args[]) { - execution_state *newes; - execution_state *nextes; - execution_state *preves; - QueryTreeList *queryTree_list; - int i; - List *planTree_list; - int nargs; - - nargs = fcache->nargs; - - newes = (execution_state *) palloc(sizeof(execution_state)); - nextes = newes; - preves = (execution_state *)NULL; - - - planTree_list = (List *) - pg_plan(fcache->src, fcache->argOidVect, nargs, &queryTree_list, None); - - for (i=0; i < queryTree_list->len; i++) { - EState *estate; - Query *queryTree = (Query*) (queryTree_list->qtrees[i]); - Plan *planTree = lfirst(planTree_list); - - if (!nextes) - nextes = (execution_state *) palloc(sizeof(execution_state)); - if (preves) - preves->next = nextes; - - nextes->next = NULL; - nextes->status = F_EXEC_START; - nextes->qd = CreateQueryDesc(queryTree, - planTree, - None); - estate = CreateExecutorState(); - - if (nargs > 0) { - int i; - ParamListInfo paramLI; - - paramLI = - (ParamListInfo)palloc((nargs+1)*sizeof(ParamListInfoData)); - - memset(paramLI, 0, nargs*sizeof(ParamListInfoData)); - - estate->es_param_list_info = paramLI; - - for (i=0; i<nargs; paramLI++, i++) { - paramLI->kind = PARAM_NUM; - paramLI->id = i+1; - paramLI->isnull = false; - paramLI->value = (Datum) NULL; - } - paramLI->kind = PARAM_INVALID; + execution_state *newes; + execution_state *nextes; + execution_state *preves; + QueryTreeList *queryTree_list; + int i; + List *planTree_list; + int nargs; + + nargs = fcache->nargs; + + newes = (execution_state *) palloc(sizeof(execution_state)); + nextes = newes; + preves = (execution_state *) NULL; + + + planTree_list = (List *) + pg_plan(fcache->src, fcache->argOidVect, nargs, &queryTree_list, None); + + for (i = 0; i < queryTree_list->len; i++) + { + EState *estate; + Query *queryTree = (Query *) (queryTree_list->qtrees[i]); + Plan *planTree = lfirst(planTree_list); + + if (!nextes) + nextes = (execution_state *) palloc(sizeof(execution_state)); + if (preves) + preves->next = nextes; + + nextes->next = NULL; + nextes->status = F_EXEC_START; + nextes->qd = CreateQueryDesc(queryTree, + planTree, + None); + estate = CreateExecutorState(); + + if (nargs > 0) + { + int i; + ParamListInfo paramLI; + + paramLI = + (ParamListInfo) palloc((nargs + 1) * sizeof(ParamListInfoData)); + + memset(paramLI, 0, nargs * sizeof(ParamListInfoData)); + + estate->es_param_list_info = paramLI; + + for (i = 0; i < nargs; paramLI++, i++) + { + paramLI->kind = PARAM_NUM; + paramLI->id = i + 1; + paramLI->isnull = false; + paramLI->value = (Datum) NULL; + } + paramLI->kind = PARAM_INVALID; + } + else + estate->es_param_list_info = (ParamListInfo) NULL; + nextes->estate = estate; + preves = nextes; + nextes = (execution_state *) NULL; + + planTree_list = lnext(planTree_list); } - else - estate->es_param_list_info = (ParamListInfo)NULL; - nextes->estate = estate; - preves = nextes; - nextes = (execution_state *)NULL; - - planTree_list = lnext(planTree_list); - } - - return newes; + + return newes; } -static TupleDesc -postquel_start(execution_state *es) +static TupleDesc +postquel_start(execution_state * es) { #ifdef FUNC_UTIL_PATCH - /* - * Do nothing for utility commands. (create, destroy...) DZ - 30-8-1996 - */ - if (es->qd->operation == CMD_UTILITY) { - return (TupleDesc) NULL; - } + + /* + * Do nothing for utility commands. (create, destroy...) DZ - + * 30-8-1996 + */ + if (es->qd->operation == CMD_UTILITY) + { + return (TupleDesc) NULL; + } #endif - return ExecutorStart(es->qd, es->estate); + return ExecutorStart(es->qd, es->estate); } static TupleTableSlot * -postquel_getnext(execution_state *es) +postquel_getnext(execution_state * es) { - int feature; - + int feature; + #ifdef FUNC_UTIL_PATCH - if (es->qd->operation == CMD_UTILITY) { - /* - * Process an utility command. (create, destroy...) DZ - 30-8-1996 - */ - ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest); - if (!LAST_POSTQUEL_COMMAND(es)) CommandCounterIncrement(); - return (TupleTableSlot*) NULL; - } + if (es->qd->operation == CMD_UTILITY) + { + + /* + * Process an utility command. (create, destroy...) DZ - + * 30-8-1996 + */ + ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest); + if (!LAST_POSTQUEL_COMMAND(es)) + CommandCounterIncrement(); + return (TupleTableSlot *) NULL; + } #endif - feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN; - - return ExecutorRun(es->qd, es->estate, feature, 0); + feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN; + + return ExecutorRun(es->qd, es->estate, feature, 0); } static void -postquel_end(execution_state *es) +postquel_end(execution_state * es) { #ifdef FUNC_UTIL_PATCH - /* - * Do nothing for utility commands. (create, destroy...) DZ - 30-8-1996 - */ - if (es->qd->operation == CMD_UTILITY) { - return; - } + + /* + * Do nothing for utility commands. (create, destroy...) DZ - + * 30-8-1996 + */ + if (es->qd->operation == CMD_UTILITY) + { + return; + } #endif - ExecutorEnd(es->qd, es->estate); + ExecutorEnd(es->qd, es->estate); } static void -postquel_sub_params(execution_state *es, - int nargs, - char *args[], - bool *nullV) +postquel_sub_params(execution_state * es, + int nargs, + char *args[], + bool * nullV) { - ParamListInfo paramLI; - EState *estate; - - estate = es->estate; - paramLI = estate->es_param_list_info; - - while (paramLI->kind != PARAM_INVALID) { - if (paramLI->kind == PARAM_NUM) { - Assert(paramLI->id <= nargs); - paramLI->value = (Datum)args[(paramLI->id - 1)]; - paramLI->isnull = nullV[(paramLI->id - 1)]; + ParamListInfo paramLI; + EState *estate; + + estate = es->estate; + paramLI = estate->es_param_list_info; + + while (paramLI->kind != PARAM_INVALID) + { + if (paramLI->kind == PARAM_NUM) + { + Assert(paramLI->id <= nargs); + paramLI->value = (Datum) args[(paramLI->id - 1)]; + paramLI->isnull = nullV[(paramLI->id - 1)]; + } + paramLI++; } - paramLI++; - } } static TupleTableSlot * copy_function_result(FunctionCachePtr fcache, - TupleTableSlot *resultSlot) + TupleTableSlot * resultSlot) { - TupleTableSlot *funcSlot; - TupleDesc resultTd; - HeapTuple newTuple; - HeapTuple oldTuple; - - Assert(! TupIsNull(resultSlot)); - oldTuple = resultSlot->val; - - funcSlot = (TupleTableSlot*)fcache->funcSlot; - - if (funcSlot == (TupleTableSlot*)NULL) - return resultSlot; - - resultTd = resultSlot->ttc_tupleDescriptor; - /* - * When the funcSlot is NULL we have to initialize the funcSlot's - * tuple descriptor. - */ - if (TupIsNull(funcSlot)) { - int i= 0; - TupleDesc funcTd = funcSlot->ttc_tupleDescriptor; - - while (i < oldTuple->t_natts) { - funcTd->attrs[i] = - (AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE); - memmove(funcTd->attrs[i], - resultTd->attrs[i], - ATTRIBUTE_TUPLE_SIZE); - i++; + TupleTableSlot *funcSlot; + TupleDesc resultTd; + HeapTuple newTuple; + HeapTuple oldTuple; + + Assert(!TupIsNull(resultSlot)); + oldTuple = resultSlot->val; + + funcSlot = (TupleTableSlot *) fcache->funcSlot; + + if (funcSlot == (TupleTableSlot *) NULL) + return resultSlot; + + resultTd = resultSlot->ttc_tupleDescriptor; + + /* + * When the funcSlot is NULL we have to initialize the funcSlot's + * tuple descriptor. + */ + if (TupIsNull(funcSlot)) + { + int i = 0; + TupleDesc funcTd = funcSlot->ttc_tupleDescriptor; + + while (i < oldTuple->t_natts) + { + funcTd->attrs[i] = + (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE); + memmove(funcTd->attrs[i], + resultTd->attrs[i], + ATTRIBUTE_TUPLE_SIZE); + i++; + } } - } - - newTuple = heap_copytuple(oldTuple); - - return ExecStoreTuple(newTuple,funcSlot,InvalidBuffer,true); + + newTuple = heap_copytuple(oldTuple); + + return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true); } -static Datum -postquel_execute(execution_state *es, - FunctionCachePtr fcache, - List *fTlist, - char **args, - bool *isNull) +static Datum +postquel_execute(execution_state * es, + FunctionCachePtr fcache, + List * fTlist, + char **args, + bool * isNull) { - TupleTableSlot *slot; - Datum value; + TupleTableSlot *slot; + Datum value; #ifdef INDEXSCAN_PATCH - /* - * It's more right place to do it (before postquel_start->ExecutorStart). - * Now ExecutorStart->ExecInitIndexScan->ExecEvalParam works ok. - * (But note: I HOPE we can do it here). - vadim 01/22/97 - */ - if (fcache->nargs > 0) - postquel_sub_params(es, fcache->nargs, args, fcache->nullVect); + + /* + * It's more right place to do it (before + * postquel_start->ExecutorStart). Now + * ExecutorStart->ExecInitIndexScan->ExecEvalParam works ok. (But + * note: I HOPE we can do it here). - vadim 01/22/97 + */ + if (fcache->nargs > 0) + postquel_sub_params(es, fcache->nargs, args, fcache->nullVect); #endif - - if (es->status == F_EXEC_START) + + if (es->status == F_EXEC_START) { - postquel_start(es); - es->status = F_EXEC_RUN; + postquel_start(es); + es->status = F_EXEC_RUN; } #ifndef INDEXSCAN_PATCH - if (fcache->nargs > 0) - postquel_sub_params(es, fcache->nargs, args, fcache->nullVect); + if (fcache->nargs > 0) + postquel_sub_params(es, fcache->nargs, args, fcache->nullVect); #endif - - slot = postquel_getnext(es); - - if (TupIsNull(slot)) { - postquel_end(es); - es->status = F_EXEC_DONE; - *isNull = true; - /* - * If this isn't the last command for the function - * we have to increment the command - * counter so that subsequent commands can see changes made - * by previous ones. - */ - if (!LAST_POSTQUEL_COMMAND(es)) CommandCounterIncrement(); - return (Datum)NULL; - } - - if (LAST_POSTQUEL_COMMAND(es)) { - TupleTableSlot *resSlot; - - /* - * Copy the result. copy_function_result is smart enough - * to do nothing when no action is called for. This helps - * reduce the logic and code redundancy here. - */ - resSlot = copy_function_result(fcache, slot); - if (fTlist != NIL) { - HeapTuple tup; - TargetEntry *tle = lfirst(fTlist); - - tup = resSlot->val; - value = ProjectAttribute(resSlot->ttc_tupleDescriptor, - tle, - tup, - isNull); - }else { - value = (Datum)resSlot; - *isNull = false; + + slot = postquel_getnext(es); + + if (TupIsNull(slot)) + { + postquel_end(es); + es->status = F_EXEC_DONE; + *isNull = true; + + /* + * If this isn't the last command for the function we have to + * increment the command counter so that subsequent commands can + * see changes made by previous ones. + */ + if (!LAST_POSTQUEL_COMMAND(es)) + CommandCounterIncrement(); + return (Datum) NULL; } - + + if (LAST_POSTQUEL_COMMAND(es)) + { + TupleTableSlot *resSlot; + + /* + * Copy the result. copy_function_result is smart enough to do + * nothing when no action is called for. This helps reduce the + * logic and code redundancy here. + */ + resSlot = copy_function_result(fcache, slot); + if (fTlist != NIL) + { + HeapTuple tup; + TargetEntry *tle = lfirst(fTlist); + + tup = resSlot->val; + value = ProjectAttribute(resSlot->ttc_tupleDescriptor, + tle, + tup, + isNull); + } + else + { + value = (Datum) resSlot; + *isNull = false; + } + + /* + * If this is a single valued function we have to end the function + * execution now. + */ + if (fcache->oneResult) + { + postquel_end(es); + es->status = F_EXEC_DONE; + } + + return value; + } + /* - * If this is a single valued function we have to end the - * function execution now. + * If this isn't the last command for the function, we don't return + * any results, but we have to increment the command counter so that + * subsequent commands can see changes made by previous ones. */ - if (fcache->oneResult) { - postquel_end(es); - es->status = F_EXEC_DONE; - } - - return value; - } - /* - * If this isn't the last command for the function, we don't - * return any results, but we have to increment the command - * counter so that subsequent commands can see changes made - * by previous ones. - */ - CommandCounterIncrement(); - return (Datum)NULL; + CommandCounterIncrement(); + return (Datum) NULL; } Datum -postquel_function(Func *funcNode, char **args, bool *isNull, bool *isDone) +postquel_function(Func * funcNode, char **args, bool * isNull, bool * isDone) { - execution_state *es; - Datum result = 0; - FunctionCachePtr fcache = funcNode->func_fcache; - CommandId savedId; - - /* - * Before we start do anything we must save CurrentScanCommandId - * to restore it before return to upper Executor. Also, we have to - * set CurrentScanCommandId equal to CurrentCommandId. - * - vadim 08/29/97 - */ - savedId = GetScanCommandId (); - SetScanCommandId (GetCurrentCommandId ()); - - es = (execution_state *) fcache->func_state; - if (es == NULL) + execution_state *es; + Datum result = 0; + FunctionCachePtr fcache = funcNode->func_fcache; + CommandId savedId; + + /* + * Before we start do anything we must save CurrentScanCommandId to + * restore it before return to upper Executor. Also, we have to set + * CurrentScanCommandId equal to CurrentCommandId. - vadim 08/29/97 + */ + savedId = GetScanCommandId(); + SetScanCommandId(GetCurrentCommandId()); + + es = (execution_state *) fcache->func_state; + if (es == NULL) { - es = init_execution_state(fcache, args); - fcache->func_state = (char *) es; + es = init_execution_state(fcache, args); + fcache->func_state = (char *) es; } - - while (es && es->status == F_EXEC_DONE) - es = es->next; - - Assert(es); - /* - * Execute each command in the function one after another until we're - * executing the final command and get a result or we run out of - * commands. - */ - while (es != (execution_state *)NULL) + + while (es && es->status == F_EXEC_DONE) + es = es->next; + + Assert(es); + + /* + * Execute each command in the function one after another until we're + * executing the final command and get a result or we run out of + * commands. + */ + while (es != (execution_state *) NULL) { - result = postquel_execute(es, - fcache, - funcNode->func_tlist, - args, - isNull); - if (es->status != F_EXEC_DONE) - break; - es = es->next; + result = postquel_execute(es, + fcache, + funcNode->func_tlist, + args, + isNull); + if (es->status != F_EXEC_DONE) + break; + es = es->next; } - - /* - * If we've gone through every command in this function, we are done. - */ - if (es == (execution_state *)NULL) - { + /* - * Reset the execution states to start over again + * If we've gone through every command in this function, we are done. */ - es = (execution_state *)fcache->func_state; - while (es) + if (es == (execution_state *) NULL) { - es->status = F_EXEC_START; - es = es->next; + + /* + * Reset the execution states to start over again + */ + es = (execution_state *) fcache->func_state; + while (es) + { + es->status = F_EXEC_START; + es = es->next; + } + + /* + * Let caller know we're finished. + */ + *isDone = true; + SetScanCommandId(savedId); + return (fcache->oneResult) ? result : (Datum) NULL; } + /* - * Let caller know we're finished. + * If we got a result from a command within the function it has to be + * the final command. All others shouldn't be returing anything. */ - *isDone = true; - SetScanCommandId (savedId); - return (fcache->oneResult) ? result : (Datum)NULL; - } - /* - * If we got a result from a command within the function it has - * to be the final command. All others shouldn't be returing - * anything. - */ - Assert ( LAST_POSTQUEL_COMMAND(es) ); - *isDone = false; - - SetScanCommandId (savedId); - return result; + Assert(LAST_POSTQUEL_COMMAND(es)); + *isDone = false; + + SetScanCommandId(savedId); + return result; } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index c6e5b269cde..ee03f6854d9 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1,17 +1,17 @@ /*------------------------------------------------------------------------- * * nodeAgg.c-- - * Routines to handle aggregate nodes. + * Routines to handle aggregate nodes. * * Copyright (c) 1994, Regents of the University of California * * * NOTE - * The implementation of Agg node has been reworked to handle legal - * SQL aggregates. (Do not expect POSTQUEL semantics.) -- ay 2/95 + * The implementation of Agg node has been reworked to handle legal + * SQL aggregates. (Do not expect POSTQUEL semantics.) -- ay 2/95 * * IDENTIFICATION - * /usr/local/devel/pglite/cvs/src/backend/executor/nodeAgg.c,v 1.13 1995/08/01 20:19:07 jolly Exp + * /usr/local/devel/pglite/cvs/src/backend/executor/nodeAgg.c,v 1.13 1995/08/01 20:19:07 jolly Exp * *------------------------------------------------------------------------- */ @@ -32,570 +32,625 @@ /* * AggFuncInfo - - * keeps the transition functions information around + * keeps the transition functions information around */ -typedef struct AggFuncInfo { - Oid xfn1_oid; - Oid xfn2_oid; - Oid finalfn_oid; - func_ptr xfn1; - func_ptr xfn2; - func_ptr finalfn; - int xfn1_nargs; - int xfn2_nargs; - int finalfn_nargs; -} AggFuncInfo; +typedef struct AggFuncInfo +{ + Oid xfn1_oid; + Oid xfn2_oid; + Oid finalfn_oid; + func_ptr xfn1; + func_ptr xfn2; + func_ptr finalfn; + int xfn1_nargs; + int xfn2_nargs; + int finalfn_nargs; +} AggFuncInfo; -static Datum aggGetAttr(TupleTableSlot *tuple, Aggreg *agg, bool *isNull); +static Datum aggGetAttr(TupleTableSlot * tuple, Aggreg * agg, bool * isNull); /* --------------------------------------- * * ExecAgg - * - * ExecAgg receives tuples from its outer subplan and aggregates over - * the appropriate attribute for each (unique) aggregate in the target - * list. (The number of tuples to aggregate over depends on whether a - * GROUP BY clause is present. It might be the number of tuples in a - * group or all the tuples that satisfy the qualifications.) The value of - * each aggregate is stored in the expression context for ExecProject to - * evaluate the result tuple. - * - * ExecAgg evaluates each aggregate in the following steps: (initcond1, - * initcond2 are the initial values and sfunc1, sfunc2, and finalfunc are - * the transition functions.) + * ExecAgg receives tuples from its outer subplan and aggregates over + * the appropriate attribute for each (unique) aggregate in the target + * list. (The number of tuples to aggregate over depends on whether a + * GROUP BY clause is present. It might be the number of tuples in a + * group or all the tuples that satisfy the qualifications.) The value of + * each aggregate is stored in the expression context for ExecProject to + * evaluate the result tuple. + * + * ExecAgg evaluates each aggregate in the following steps: (initcond1, + * initcond2 are the initial values and sfunc1, sfunc2, and finalfunc are + * the transition functions.) * - * value1[i] = initcond1 - * value2[i] = initcond2 - * forall tuples do - * value1[i] = sfunc1(aggregate_attribute, value1[i]) - * value2[i] = sfunc2(value2[i]) - * value1[i] = finalfunc(value1[i], value2[i]) + * value1[i] = initcond1 + * value2[i] = initcond2 + * forall tuples do + * value1[i] = sfunc1(aggregate_attribute, value1[i]) + * value2[i] = sfunc2(value2[i]) + * value1[i] = finalfunc(value1[i], value2[i]) * - * If the outer subplan is a Group node, ExecAgg returns as many tuples - * as there are groups. + * If the outer subplan is a Group node, ExecAgg returns as many tuples + * as there are groups. * - * XXX handling of NULL doesn't work + * XXX handling of NULL doesn't work * - * OLD COMMENTS + * OLD COMMENTS * - * XXX Aggregates should probably have another option: what to do - * with transfn2 if we hit a null value. "count" (transfn1 = null, - * transfn2 = increment) will want to have transfn2 called; "avg" - * (transfn1 = add, transfn2 = increment) will not. -pma 1/3/93 + * XXX Aggregates should probably have another option: what to do + * with transfn2 if we hit a null value. "count" (transfn1 = null, + * transfn2 = increment) will want to have transfn2 called; "avg" + * (transfn1 = add, transfn2 = increment) will not. -pma 1/3/93 * * ------------------------------------------ */ TupleTableSlot * -ExecAgg(Agg *node) +ExecAgg(Agg * node) { - AggState *aggstate; - EState *estate; - Aggreg **aggregates; - Plan *outerPlan; - int i, nagg; - Datum *value1, *value2; - int *noInitValue; - AggFuncInfo *aggFuncInfo; - long nTuplesAgged = 0; - ExprContext *econtext; - ProjectionInfo *projInfo; - TupleTableSlot *resultSlot; - HeapTuple oneTuple; - char* nulls; - bool isDone; - bool isNull = FALSE, isNull1 = FALSE, isNull2 = FALSE; - - /* --------------------- - * get state info from node - * --------------------- - */ - aggstate = node->aggstate; - if (aggstate->agg_done) - return NULL; - - estate = node->plan.state; - econtext = aggstate->csstate.cstate.cs_ExprContext; - aggregates = node->aggs; - nagg = node->numAgg; - - value1 = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_values; - nulls = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_nulls; - - value2 = (Datum *)palloc(sizeof(Datum) * nagg); - memset(value2, 0, sizeof(Datum) * nagg); - - aggFuncInfo = (AggFuncInfo *)palloc(sizeof(AggFuncInfo) * nagg); - memset(aggFuncInfo, 0, sizeof(AggFuncInfo) * nagg); - - noInitValue = (int *)palloc(sizeof(int) * nagg); - memset(noInitValue, 0, sizeof(noInitValue) * nagg); - - outerPlan = outerPlan(node); - oneTuple = NULL; - - projInfo = aggstate->csstate.cstate.cs_ProjInfo; - - for(i = 0; i < nagg; i++) { - Aggreg *agg; - char *aggname; - HeapTuple aggTuple; - Form_pg_aggregate aggp; - Oid xfn1_oid, xfn2_oid, finalfn_oid; - func_ptr xfn1_ptr, xfn2_ptr, finalfn_ptr; - int xfn1_nargs, xfn2_nargs, finalfn_nargs; - - agg = aggregates[i]; + AggState *aggstate; + EState *estate; + Aggreg **aggregates; + Plan *outerPlan; + int i, + nagg; + Datum *value1, + *value2; + int *noInitValue; + AggFuncInfo *aggFuncInfo; + long nTuplesAgged = 0; + ExprContext *econtext; + ProjectionInfo *projInfo; + TupleTableSlot *resultSlot; + HeapTuple oneTuple; + char *nulls; + bool isDone; + bool isNull = FALSE, + isNull1 = FALSE, + isNull2 = FALSE; /* --------------------- - * find transfer functions of all the aggregates and initialize - * their initial values + * get state info from node * --------------------- */ - aggname = agg->aggname; - aggTuple = SearchSysCacheTuple(AGGNAME, - PointerGetDatum(aggname), - ObjectIdGetDatum(agg->basetype), - 0,0); - if (!HeapTupleIsValid(aggTuple)) - elog(WARN, "ExecAgg: cache lookup failed for aggregate \"%s\"(%s)", - aggname, - tname(get_id_type(agg->basetype))); - aggp = (Form_pg_aggregate) GETSTRUCT(aggTuple); - - xfn1_oid = aggp->aggtransfn1; - xfn2_oid = aggp->aggtransfn2; - finalfn_oid = aggp->aggfinalfn; - - if (OidIsValid(finalfn_oid)) { - fmgr_info(finalfn_oid, &finalfn_ptr, &finalfn_nargs); - aggFuncInfo[i].finalfn_oid = finalfn_oid; - aggFuncInfo[i].finalfn = finalfn_ptr; - aggFuncInfo[i].finalfn_nargs = finalfn_nargs; - } + aggstate = node->aggstate; + if (aggstate->agg_done) + return NULL; + + estate = node->plan.state; + econtext = aggstate->csstate.cstate.cs_ExprContext; + aggregates = node->aggs; + nagg = node->numAgg; + + value1 = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_values; + nulls = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_nulls; + + value2 = (Datum *) palloc(sizeof(Datum) * nagg); + memset(value2, 0, sizeof(Datum) * nagg); + + aggFuncInfo = (AggFuncInfo *) palloc(sizeof(AggFuncInfo) * nagg); + memset(aggFuncInfo, 0, sizeof(AggFuncInfo) * nagg); + + noInitValue = (int *) palloc(sizeof(int) * nagg); + memset(noInitValue, 0, sizeof(noInitValue) * nagg); + + outerPlan = outerPlan(node); + oneTuple = NULL; + + projInfo = aggstate->csstate.cstate.cs_ProjInfo; + + for (i = 0; i < nagg; i++) + { + Aggreg *agg; + char *aggname; + HeapTuple aggTuple; + Form_pg_aggregate aggp; + Oid xfn1_oid, + xfn2_oid, + finalfn_oid; + func_ptr xfn1_ptr, + xfn2_ptr, + finalfn_ptr; + int xfn1_nargs, + xfn2_nargs, + finalfn_nargs; + + agg = aggregates[i]; + + /* --------------------- + * find transfer functions of all the aggregates and initialize + * their initial values + * --------------------- + */ + aggname = agg->aggname; + aggTuple = SearchSysCacheTuple(AGGNAME, + PointerGetDatum(aggname), + ObjectIdGetDatum(agg->basetype), + 0, 0); + if (!HeapTupleIsValid(aggTuple)) + elog(WARN, "ExecAgg: cache lookup failed for aggregate \"%s\"(%s)", + aggname, + tname(get_id_type(agg->basetype))); + aggp = (Form_pg_aggregate) GETSTRUCT(aggTuple); + + xfn1_oid = aggp->aggtransfn1; + xfn2_oid = aggp->aggtransfn2; + finalfn_oid = aggp->aggfinalfn; + + if (OidIsValid(finalfn_oid)) + { + fmgr_info(finalfn_oid, &finalfn_ptr, &finalfn_nargs); + aggFuncInfo[i].finalfn_oid = finalfn_oid; + aggFuncInfo[i].finalfn = finalfn_ptr; + aggFuncInfo[i].finalfn_nargs = finalfn_nargs; + } - if (OidIsValid(xfn2_oid)) { - fmgr_info(xfn2_oid, &xfn2_ptr, &xfn2_nargs); - aggFuncInfo[i].xfn2_oid = xfn2_oid; - aggFuncInfo[i].xfn2 = xfn2_ptr; - aggFuncInfo[i].xfn2_nargs = xfn2_nargs; - value2[i] = (Datum)AggNameGetInitVal((char*)aggname, - aggp->aggbasetype, - 2, - &isNull2); - /* ------------------------------------------ - * If there is a second transition function, its initial - * value must exist -- as it does not depend on data values, - * we have no other way of determining an initial value. - * ------------------------------------------ - */ - if (isNull2) - elog(WARN, "ExecAgg: agginitval2 is null"); - } + if (OidIsValid(xfn2_oid)) + { + fmgr_info(xfn2_oid, &xfn2_ptr, &xfn2_nargs); + aggFuncInfo[i].xfn2_oid = xfn2_oid; + aggFuncInfo[i].xfn2 = xfn2_ptr; + aggFuncInfo[i].xfn2_nargs = xfn2_nargs; + value2[i] = (Datum) AggNameGetInitVal((char *) aggname, + aggp->aggbasetype, + 2, + &isNull2); + /* ------------------------------------------ + * If there is a second transition function, its initial + * value must exist -- as it does not depend on data values, + * we have no other way of determining an initial value. + * ------------------------------------------ + */ + if (isNull2) + elog(WARN, "ExecAgg: agginitval2 is null"); + } - if (OidIsValid(xfn1_oid)) { - fmgr_info(xfn1_oid, &xfn1_ptr, &xfn1_nargs); - aggFuncInfo[i].xfn1_oid = xfn1_oid; - aggFuncInfo[i].xfn1 = xfn1_ptr; - aggFuncInfo[i].xfn1_nargs = xfn1_nargs; - value1[i] = (Datum)AggNameGetInitVal((char*)aggname, - aggp->aggbasetype, - 1, - &isNull1); - - /* ------------------------------------------ - * If the initial value for the first transition function - * doesn't exist in the pg_aggregate table then we let - * the first value returned from the outer procNode become - * the initial value. (This is useful for aggregates like - * max{} and min{}.) - * ------------------------------------------ - */ - if (isNull1) { - noInitValue[i] = 1; - nulls[i] = 1; - } - } - } - - /* ---------------- - * for each tuple from the the outer plan, apply all the aggregates - * ---------------- - */ - for (;;) { - HeapTuple outerTuple = NULL; - TupleTableSlot *outerslot; - - isNull = isNull1 = isNull2 = 0; - outerslot = ExecProcNode(outerPlan, (Plan*)node); - if (outerslot) outerTuple = outerslot->val; - if (!HeapTupleIsValid(outerTuple)) { - /* when the outerplan doesn't return a single tuple, - create a dummy heaptuple anyway - because we still need to return a valid aggregate value. - The value returned will be the initial values of the - transition functions */ - if (nTuplesAgged == 0) { - TupleDesc tupType; - Datum *tupValue; - char* null_array; - - tupType = aggstate->csstate.css_ScanTupleSlot->ttc_tupleDescriptor; - tupValue = projInfo->pi_tupValue; - - /* initially, set all the values to NULL */ - null_array = malloc(tupType->natts); - for (i=0;i<tupType->natts;i++) - null_array[i] = 'n'; - oneTuple = heap_formtuple(tupType, tupValue, null_array); - free(null_array); - } - break; + if (OidIsValid(xfn1_oid)) + { + fmgr_info(xfn1_oid, &xfn1_ptr, &xfn1_nargs); + aggFuncInfo[i].xfn1_oid = xfn1_oid; + aggFuncInfo[i].xfn1 = xfn1_ptr; + aggFuncInfo[i].xfn1_nargs = xfn1_nargs; + value1[i] = (Datum) AggNameGetInitVal((char *) aggname, + aggp->aggbasetype, + 1, + &isNull1); + + /* ------------------------------------------ + * If the initial value for the first transition function + * doesn't exist in the pg_aggregate table then we let + * the first value returned from the outer procNode become + * the initial value. (This is useful for aggregates like + * max{} and min{}.) + * ------------------------------------------ + */ + if (isNull1) + { + noInitValue[i] = 1; + nulls[i] = 1; + } + } } - for(i = 0; i < nagg; i++) { - AttrNumber attnum; - int2 attlen; - Datum newVal = (Datum) NULL; - AggFuncInfo *aggfns = &aggFuncInfo[i]; - Datum args[2]; - Node *tagnode = NULL; - - switch(nodeTag(aggregates[i]->target)) - { - case T_Var: - tagnode = NULL; - newVal = aggGetAttr(outerslot, - aggregates[i], - &isNull); - break; - case T_Expr: - tagnode = ((Expr*)aggregates[i]->target)->oper; - econtext->ecxt_scantuple = outerslot; - newVal = ExecEvalExpr (aggregates[i]->target, econtext, - &isNull, NULL); + /* ---------------- + * for each tuple from the the outer plan, apply all the aggregates + * ---------------- + */ + for (;;) + { + HeapTuple outerTuple = NULL; + TupleTableSlot *outerslot; + + isNull = isNull1 = isNull2 = 0; + outerslot = ExecProcNode(outerPlan, (Plan *) node); + if (outerslot) + outerTuple = outerslot->val; + if (!HeapTupleIsValid(outerTuple)) + { + + /* + * when the outerplan doesn't return a single tuple, create a + * dummy heaptuple anyway because we still need to return a + * valid aggregate value. The value returned will be the + * initial values of the transition functions + */ + if (nTuplesAgged == 0) + { + TupleDesc tupType; + Datum *tupValue; + char *null_array; + + tupType = aggstate->csstate.css_ScanTupleSlot->ttc_tupleDescriptor; + tupValue = projInfo->pi_tupValue; + + /* initially, set all the values to NULL */ + null_array = malloc(tupType->natts); + for (i = 0; i < tupType->natts; i++) + null_array[i] = 'n'; + oneTuple = heap_formtuple(tupType, tupValue, null_array); + free(null_array); + } break; - default: - elog(WARN, "ExecAgg: Bad Agg->Target for Agg %d", i); - } - - if (isNull) - continue; /* ignore this tuple for this agg */ - - if (aggfns->xfn1) { - if (noInitValue[i]) { - int byVal; - - /* - * value1 and value2 has not been initialized. This - * is the first non-NULL value. We use it as the - * initial value. - */ - /* but we can't just use it straight, we have - to make a copy of it since the tuple from which - it came will be freed on the next iteration - of the scan */ - if ( tagnode != NULL ) - { - FunctionCachePtr fcache_ptr; - - if ( nodeTag(tagnode) == T_Func ) - fcache_ptr = ((Func*)tagnode)->func_fcache; - else - fcache_ptr = ((Oper*)tagnode)->op_fcache; - attlen = fcache_ptr->typlen; - byVal = fcache_ptr->typbyval; - } - else - { - attnum = ((Var*)aggregates[i]->target)->varattno; - attlen = outerslot->ttc_tupleDescriptor->attrs[attnum-1]->attlen; - byVal = outerslot->ttc_tupleDescriptor->attrs[attnum-1]->attbyval; - } - if (attlen == -1) { - /* variable length */ - attlen = VARSIZE((struct varlena*) newVal); - } - value1[i] = (Datum)palloc(attlen); - if ( byVal ) - value1[i] = newVal; - else - memmove((char*)(value1[i]), (char*)newVal, attlen); - /* value1[i] = newVal; */ - noInitValue[i] = 0; - nulls[i] = 0; - } else { - /* - * apply the transition functions. - */ - args[0] = value1[i]; - args[1] = newVal; - value1[i] = - (Datum)fmgr_c(aggfns->xfn1, aggfns->xfn1_oid, - aggfns->xfn1_nargs, (FmgrValues *)args, - &isNull1); - Assert(!isNull1); } - } - - if (aggfns->xfn2) { - Datum xfn2_val = value2[i]; - - value2[i] = - (Datum)fmgr_c(aggfns->xfn2, aggfns->xfn2_oid, - aggfns->xfn2_nargs, - (FmgrValues *)&xfn2_val, &isNull2); - Assert(!isNull2); - } + + for (i = 0; i < nagg; i++) + { + AttrNumber attnum; + int2 attlen; + Datum newVal = (Datum) NULL; + AggFuncInfo *aggfns = &aggFuncInfo[i]; + Datum args[2]; + Node *tagnode = NULL; + + switch (nodeTag(aggregates[i]->target)) + { + case T_Var: + tagnode = NULL; + newVal = aggGetAttr(outerslot, + aggregates[i], + &isNull); + break; + case T_Expr: + tagnode = ((Expr *) aggregates[i]->target)->oper; + econtext->ecxt_scantuple = outerslot; + newVal = ExecEvalExpr(aggregates[i]->target, econtext, + &isNull, NULL); + break; + default: + elog(WARN, "ExecAgg: Bad Agg->Target for Agg %d", i); + } + + if (isNull) + continue; /* ignore this tuple for this agg */ + + if (aggfns->xfn1) + { + if (noInitValue[i]) + { + int byVal; + + /* + * value1 and value2 has not been initialized. This is + * the first non-NULL value. We use it as the initial + * value. + */ + + /* + * but we can't just use it straight, we have to make + * a copy of it since the tuple from which it came + * will be freed on the next iteration of the scan + */ + if (tagnode != NULL) + { + FunctionCachePtr fcache_ptr; + + if (nodeTag(tagnode) == T_Func) + fcache_ptr = ((Func *) tagnode)->func_fcache; + else + fcache_ptr = ((Oper *) tagnode)->op_fcache; + attlen = fcache_ptr->typlen; + byVal = fcache_ptr->typbyval; + } + else + { + attnum = ((Var *) aggregates[i]->target)->varattno; + attlen = outerslot->ttc_tupleDescriptor->attrs[attnum - 1]->attlen; + byVal = outerslot->ttc_tupleDescriptor->attrs[attnum - 1]->attbyval; + } + if (attlen == -1) + { + /* variable length */ + attlen = VARSIZE((struct varlena *) newVal); + } + value1[i] = (Datum) palloc(attlen); + if (byVal) + value1[i] = newVal; + else + memmove((char *) (value1[i]), (char *) newVal, attlen); + /* value1[i] = newVal; */ + noInitValue[i] = 0; + nulls[i] = 0; + } + else + { + + /* + * apply the transition functions. + */ + args[0] = value1[i]; + args[1] = newVal; + value1[i] = + (Datum) fmgr_c(aggfns->xfn1, aggfns->xfn1_oid, + aggfns->xfn1_nargs, (FmgrValues *) args, + &isNull1); + Assert(!isNull1); + } + } + + if (aggfns->xfn2) + { + Datum xfn2_val = value2[i]; + + value2[i] = + (Datum) fmgr_c(aggfns->xfn2, aggfns->xfn2_oid, + aggfns->xfn2_nargs, + (FmgrValues *) & xfn2_val, &isNull2); + Assert(!isNull2); + } + } + + /* + * keep this for the projection (we only need one of these - all + * the tuples we aggregate over share the same group column) + */ + if (!oneTuple) + { + oneTuple = heap_copytuple(outerslot->val); + } + + nTuplesAgged++; + } + + /* -------------- + * finalize the aggregate (if necessary), and get the resultant value + * -------------- + */ + for (i = 0; i < nagg; i++) + { + char *args[2]; + AggFuncInfo *aggfns = &aggFuncInfo[i]; + + if (noInitValue[i]) + { + + /* + * No values found for this agg; return current state. This + * seems to fix behavior for avg() aggregate. -tgl 12/96 + */ + } + else if (aggfns->finalfn && nTuplesAgged > 0) + { + if (aggfns->finalfn_nargs > 1) + { + args[0] = (char *) value1[i]; + args[1] = (char *) value2[i]; + } + else if (aggfns->xfn1) + { + args[0] = (char *) value1[i]; + } + else if (aggfns->xfn2) + { + args[0] = (char *) value2[i]; + } + else + elog(WARN, "ExecAgg: no valid transition functions??"); + value1[i] = + (Datum) fmgr_c(aggfns->finalfn, aggfns->finalfn_oid, + aggfns->finalfn_nargs, (FmgrValues *) args, + &(nulls[i])); + } + else if (aggfns->xfn1) + { + + /* + * value in the right place, ignore. (If you remove this case, + * fix the else part. -ay 2/95) + */ + } + else if (aggfns->xfn2) + { + value1[i] = value2[i]; + } + else + elog(WARN, "ExecAgg: no valid transition functions??"); } /* - * keep this for the projection (we only need one of these - - * all the tuples we aggregate over share the same group column) + * whether the aggregation is done depends on whether we are doing + * aggregation over groups or the entire table */ - if (!oneTuple) { - oneTuple = heap_copytuple(outerslot->val); + if (nodeTag(outerPlan) == T_Group) + { + /* aggregation over groups */ + aggstate->agg_done = ((Group *) outerPlan)->grpstate->grp_done; + } + else + { + aggstate->agg_done = TRUE; } - nTuplesAgged++; - } - - /* -------------- - * finalize the aggregate (if necessary), and get the resultant value - * -------------- - */ - for(i = 0; i < nagg; i++) { - char *args[2]; - AggFuncInfo *aggfns = &aggFuncInfo[i]; - - if (noInitValue[i]) { - /* - * No values found for this agg; return current state. - * This seems to fix behavior for avg() aggregate. -tgl 12/96 - */ - } else if (aggfns->finalfn && nTuplesAgged > 0) { - if (aggfns->finalfn_nargs > 1) { - args[0] = (char*)value1[i]; - args[1] = (char*)value2[i]; - } else if (aggfns->xfn1) { - args[0] = (char*)value1[i]; - } else if (aggfns->xfn2) { - args[0] = (char*)value2[i]; - } else - elog(WARN, "ExecAgg: no valid transition functions??"); - value1[i] = - (Datum)fmgr_c(aggfns->finalfn, aggfns->finalfn_oid, - aggfns->finalfn_nargs, (FmgrValues *) args, - &(nulls[i])); - } else if (aggfns->xfn1) { - /* - * value in the right place, ignore. (If you remove this - * case, fix the else part. -ay 2/95) - */ - } else if (aggfns->xfn2) { - value1[i] = value2[i]; - } else - elog(WARN, "ExecAgg: no valid transition functions??"); - } - - /* - * whether the aggregation is done depends on whether we are doing - * aggregation over groups or the entire table - */ - if (nodeTag(outerPlan)==T_Group) { - /* aggregation over groups */ - aggstate->agg_done = ((Group*)outerPlan)->grpstate->grp_done; - } else { - aggstate->agg_done = TRUE; - } - - /* ---------------- - * form a projection tuple, store it in the result tuple - * slot and return it. - * ---------------- - */ - ExecStoreTuple(oneTuple, - aggstate->csstate.css_ScanTupleSlot, - InvalidBuffer, - false); - econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot; - resultSlot = ExecProject(projInfo, &isDone); - - if (oneTuple) - pfree(oneTuple); - - return resultSlot; + /* ---------------- + * form a projection tuple, store it in the result tuple + * slot and return it. + * ---------------- + */ + ExecStoreTuple(oneTuple, + aggstate->csstate.css_ScanTupleSlot, + InvalidBuffer, + false); + econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot; + resultSlot = ExecProject(projInfo, &isDone); + + if (oneTuple) + pfree(oneTuple); + + return resultSlot; } /* ----------------- * ExecInitAgg * - * Creates the run-time information for the agg node produced by the - * planner and initializes its outer subtree + * Creates the run-time information for the agg node produced by the + * planner and initializes its outer subtree * ----------------- */ bool -ExecInitAgg(Agg *node, EState *estate, Plan *parent) +ExecInitAgg(Agg * node, EState * estate, Plan * parent) { - AggState *aggstate; - Plan *outerPlan; - ExprContext *econtext; - - /* - * assign the node's execution state - */ - node->plan.state = estate; - - /* - * create state structure - */ - aggstate = makeNode(AggState); - node->aggstate = aggstate; - aggstate->agg_done = FALSE; - - /* - * assign node's base id and create expression context - */ - ExecAssignNodeBaseInfo(estate, &aggstate->csstate.cstate, - (Plan*) parent); - ExecAssignExprContext(estate, &aggstate->csstate.cstate); - + AggState *aggstate; + Plan *outerPlan; + ExprContext *econtext; + + /* + * assign the node's execution state + */ + node->plan.state = estate; + + /* + * create state structure + */ + aggstate = makeNode(AggState); + node->aggstate = aggstate; + aggstate->agg_done = FALSE; + + /* + * assign node's base id and create expression context + */ + ExecAssignNodeBaseInfo(estate, &aggstate->csstate.cstate, + (Plan *) parent); + ExecAssignExprContext(estate, &aggstate->csstate.cstate); + #define AGG_NSLOTS 2 - /* - * tuple table initialization - */ - ExecInitScanTupleSlot(estate, &aggstate->csstate); - ExecInitResultTupleSlot(estate, &aggstate->csstate.cstate); - - econtext = aggstate->csstate.cstate.cs_ExprContext; - econtext->ecxt_values = - (Datum *)palloc(sizeof(Datum) * node->numAgg); - memset(econtext->ecxt_values, 0, sizeof(Datum) * node->numAgg); - econtext->ecxt_nulls = (char *)palloc(node->numAgg); - memset(econtext->ecxt_nulls, 0, node->numAgg); - - /* - * initializes child nodes - */ - outerPlan = outerPlan(node); - ExecInitNode(outerPlan, estate, (Plan *)node); - - /* ---------------- - * initialize tuple type. - * ---------------- - */ - ExecAssignScanTypeFromOuterPlan((Plan *) node, &aggstate->csstate); - - /* - * Initialize tuple type for both result and scan. - * This node does no projection - */ - ExecAssignResultTypeFromTL((Plan*) node, &aggstate->csstate.cstate); - ExecAssignProjectionInfo((Plan*)node, &aggstate->csstate.cstate); - - return TRUE; + + /* + * tuple table initialization + */ + ExecInitScanTupleSlot(estate, &aggstate->csstate); + ExecInitResultTupleSlot(estate, &aggstate->csstate.cstate); + + econtext = aggstate->csstate.cstate.cs_ExprContext; + econtext->ecxt_values = + (Datum *) palloc(sizeof(Datum) * node->numAgg); + memset(econtext->ecxt_values, 0, sizeof(Datum) * node->numAgg); + econtext->ecxt_nulls = (char *) palloc(node->numAgg); + memset(econtext->ecxt_nulls, 0, node->numAgg); + + /* + * initializes child nodes + */ + outerPlan = outerPlan(node); + ExecInitNode(outerPlan, estate, (Plan *) node); + + /* ---------------- + * initialize tuple type. + * ---------------- + */ + ExecAssignScanTypeFromOuterPlan((Plan *) node, &aggstate->csstate); + + /* + * Initialize tuple type for both result and scan. This node does no + * projection + */ + ExecAssignResultTypeFromTL((Plan *) node, &aggstate->csstate.cstate); + ExecAssignProjectionInfo((Plan *) node, &aggstate->csstate.cstate); + + return TRUE; } int -ExecCountSlotsAgg(Agg *node) +ExecCountSlotsAgg(Agg * node) { - return ExecCountSlotsNode(outerPlan(node)) + + return ExecCountSlotsNode(outerPlan(node)) + ExecCountSlotsNode(innerPlan(node)) + - AGG_NSLOTS; + AGG_NSLOTS; } /* ------------------------ - * ExecEndAgg(node) + * ExecEndAgg(node) * * ----------------------- */ void -ExecEndAgg(Agg *node) +ExecEndAgg(Agg * node) { - AggState *aggstate; - Plan *outerPlan; + AggState *aggstate; + Plan *outerPlan; - aggstate = node->aggstate; + aggstate = node->aggstate; - ExecFreeProjectionInfo(&aggstate->csstate.cstate); + ExecFreeProjectionInfo(&aggstate->csstate.cstate); - outerPlan = outerPlan(node); - ExecEndNode(outerPlan, (Plan*)node); - - /* clean up tuple table */ - ExecClearTuple(aggstate->csstate.css_ScanTupleSlot); + outerPlan = outerPlan(node); + ExecEndNode(outerPlan, (Plan *) node); + + /* clean up tuple table */ + ExecClearTuple(aggstate->csstate.css_ScanTupleSlot); } /***************************************************************************** - * Support Routines + * Support Routines *****************************************************************************/ /* * aggGetAttr - - * get the attribute (specified in the Var node in agg) to aggregate - * over from the tuple + * get the attribute (specified in the Var node in agg) to aggregate + * over from the tuple */ -static Datum -aggGetAttr(TupleTableSlot *slot, - Aggreg *agg, - bool *isNull) +static Datum +aggGetAttr(TupleTableSlot * slot, + Aggreg * agg, + bool * isNull) { - Datum result; - AttrNumber attnum; - HeapTuple heapTuple; - TupleDesc tuple_type; - Buffer buffer; - - /* ---------------- - * extract tuple information from the slot - * ---------------- - */ - heapTuple = slot->val; - tuple_type = slot->ttc_tupleDescriptor; - buffer = slot->ttc_buffer; - - attnum = ((Var*)agg->target)->varattno; - - /* - * If the attribute number is invalid, then we are supposed to - * return the entire tuple, we give back a whole slot so that - * callers know what the tuple looks like. - */ - if (attnum == InvalidAttrNumber) { - TupleTableSlot *tempSlot; - TupleDesc td; - HeapTuple tup; - - tempSlot = makeNode(TupleTableSlot); - tempSlot->ttc_shouldFree = false; - tempSlot->ttc_descIsNew = true; - tempSlot->ttc_tupleDescriptor = (TupleDesc)NULL, - tempSlot->ttc_buffer = InvalidBuffer; - tempSlot->ttc_whichplan = -1; - - tup = heap_copytuple(slot->val); - td = CreateTupleDescCopy(slot->ttc_tupleDescriptor); - - ExecSetSlotDescriptor(tempSlot, td); - - ExecStoreTuple(tup, tempSlot, InvalidBuffer, true); - return (Datum) tempSlot; - } - - result = (Datum) - heap_getattr(heapTuple, /* tuple containing attribute */ - buffer, /* buffer associated with tuple */ - attnum, /* attribute number of desired attribute */ - tuple_type, /* tuple descriptor of tuple */ - isNull); /* return: is attribute null? */ - - /* ---------------- - * return null if att is null - * ---------------- - */ - if (*isNull) - return (Datum) NULL; - - return result; + Datum result; + AttrNumber attnum; + HeapTuple heapTuple; + TupleDesc tuple_type; + Buffer buffer; + + /* ---------------- + * extract tuple information from the slot + * ---------------- + */ + heapTuple = slot->val; + tuple_type = slot->ttc_tupleDescriptor; + buffer = slot->ttc_buffer; + + attnum = ((Var *) agg->target)->varattno; + + /* + * If the attribute number is invalid, then we are supposed to return + * the entire tuple, we give back a whole slot so that callers know + * what the tuple looks like. + */ + if (attnum == InvalidAttrNumber) + { + TupleTableSlot *tempSlot; + TupleDesc td; + HeapTuple tup; + + tempSlot = makeNode(TupleTableSlot); + tempSlot->ttc_shouldFree = false; + tempSlot->ttc_descIsNew = true; + tempSlot->ttc_tupleDescriptor = (TupleDesc) NULL, + tempSlot->ttc_buffer = InvalidBuffer; + tempSlot->ttc_whichplan = -1; + + tup = heap_copytuple(slot->val); + td = CreateTupleDescCopy(slot->ttc_tupleDescriptor); + + ExecSetSlotDescriptor(tempSlot, td); + + ExecStoreTuple(tup, tempSlot, InvalidBuffer, true); + return (Datum) tempSlot; + } + + result = (Datum) + heap_getattr(heapTuple, /* tuple containing attribute */ + buffer, /* buffer associated with tuple */ + attnum, /* attribute number of desired attribute */ + tuple_type,/* tuple descriptor of tuple */ + isNull); /* return: is attribute null? */ + + /* ---------------- + * return null if att is null + * ---------------- + */ + if (*isNull) + return (Datum) NULL; + + return result; } diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index 7abc6d91744..043ad5d9743 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -1,56 +1,56 @@ /*------------------------------------------------------------------------- * * nodeAppend.c-- - * routines to handle append nodes. + * routines to handle append nodes. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.5 1997/08/19 21:31:07 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.6 1997/09/07 04:41:30 momjian Exp $ * *------------------------------------------------------------------------- */ /* INTERFACE ROUTINES - * ExecInitAppend - initialize the append node - * ExecProcAppend - retrieve the next tuple from the node - * ExecEndAppend - shut down the append node + * ExecInitAppend - initialize the append node + * ExecProcAppend - retrieve the next tuple from the node + * ExecEndAppend - shut down the append node * - * NOTES - * Each append node contains a list of one or more subplans which - * must be iteratively processed (forwards or backwards). - * Tuples are retrieved by executing the 'whichplan'th subplan - * until the subplan stops returning tuples, at which point that - * plan is shut down and the next started up. + * NOTES + * Each append node contains a list of one or more subplans which + * must be iteratively processed (forwards or backwards). + * Tuples are retrieved by executing the 'whichplan'th subplan + * until the subplan stops returning tuples, at which point that + * plan is shut down and the next started up. * - * Append nodes don't make use of their left and right - * subtrees, rather they maintain a list of subplans so - * a typical append node looks like this in the plan tree: + * Append nodes don't make use of their left and right + * subtrees, rather they maintain a list of subplans so + * a typical append node looks like this in the plan tree: * - * ... - * / - * Append -------+------+------+--- nil - * / \ | | | - * nil nil ... ... ... - * subplans + * ... + * / + * Append -------+------+------+--- nil + * / \ | | | + * nil nil ... ... ... + * subplans * - * Append nodes are currently used to support inheritance - * queries, where several relations need to be scanned. - * For example, in our standard person/student/employee/student-emp - * example, where student and employee inherit from person - * and student-emp inherits from student and employee, the - * query: + * Append nodes are currently used to support inheritance + * queries, where several relations need to be scanned. + * For example, in our standard person/student/employee/student-emp + * example, where student and employee inherit from person + * and student-emp inherits from student and employee, the + * query: * - * retrieve (e.name) from e in person* + * retrieve (e.name) from e in person* * - * generates the plan: + * generates the plan: * - * | - * Append -------+-------+--------+--------+ - * / \ | | | | - * nil nil Scan Scan Scan Scan - * | | | | - * person employee student student-emp + * | + * Append -------+-------+--------+--------+ + * / \ | | | | + * nil nil Scan Scan Scan Scan + * | | | | + * person employee student student-emp */ #include "postgres.h" @@ -62,429 +62,451 @@ #include "executor/nodeIndexscan.h" #include "utils/palloc.h" #include "utils/mcxt.h" -#include "parser/parsetree.h" /* for rt_store() macro */ +#include "parser/parsetree.h" /* for rt_store() macro */ -static bool exec_append_initialize_next(Append *node); +static bool exec_append_initialize_next(Append * node); /* ---------------------------------------------------------------- - * exec-append-initialize-next - * - * Sets up the append node state (i.e. the append state node) - * for the "next" scan. - * - * Returns t iff there is a "next" scan to process. + * exec-append-initialize-next + * + * Sets up the append node state (i.e. the append state node) + * for the "next" scan. + * + * Returns t iff there is a "next" scan to process. * ---------------------------------------------------------------- */ -static bool -exec_append_initialize_next(Append *node) +static bool +exec_append_initialize_next(Append * node) { - EState *estate; - AppendState *unionstate; - TupleTableSlot *result_slot; - List *rangeTable; - - int whichplan; - int nplans; - List *rtentries; - ResTarget *rtentry; - - Index unionrelid; - - /* ---------------- - * get information from the append node - * ---------------- - */ - estate = node->plan.state; - unionstate = node->unionstate; - result_slot = unionstate->cstate.cs_ResultTupleSlot; - rangeTable = estate->es_range_table; - - whichplan = unionstate->as_whichplan; - nplans = unionstate->as_nplans; - rtentries = node->unionrtentries; - - if (whichplan < 0) { - /* ---------------- - * if scanning in reverse, we start at - * the last scan in the list and then - * proceed back to the first.. in any case - * we inform ExecProcAppend that we are - * at the end of the line by returning FALSE - * ---------------- - */ - unionstate->as_whichplan = 0; - return FALSE; - - } else if (whichplan >= nplans) { - /* ---------------- - * as above, end the scan if we go beyond - * the last scan in our list.. - * ---------------- - */ - unionstate->as_whichplan = nplans - 1; - return FALSE; - - } else { - /* ---------------- - * initialize the scan - * (and update the range table appropriately) - * (doesn't this leave the range table hosed for anybody upstream - * of the Append node??? - jolly ) - * ---------------- - */ - if (node->unionrelid > 0) { - rtentry = nth(whichplan, rtentries); - if (rtentry == NULL) - elog(DEBUG, "exec_append_initialize_next: rtentry is nil"); - - unionrelid = node->unionrelid; - - rt_store(unionrelid, rangeTable, rtentry); - - if (unionstate->as_junkFilter_list) { - estate->es_junkFilter = - (JunkFilter*)nth(whichplan, - unionstate->as_junkFilter_list); - } - if (unionstate->as_result_relation_info_list) { - estate->es_result_relation_info = - (RelationInfo*) nth(whichplan, - unionstate->as_result_relation_info_list); - } - result_slot->ttc_whichplan = whichplan; + EState *estate; + AppendState *unionstate; + TupleTableSlot *result_slot; + List *rangeTable; + + int whichplan; + int nplans; + List *rtentries; + ResTarget *rtentry; + + Index unionrelid; + + /* ---------------- + * get information from the append node + * ---------------- + */ + estate = node->plan.state; + unionstate = node->unionstate; + result_slot = unionstate->cstate.cs_ResultTupleSlot; + rangeTable = estate->es_range_table; + + whichplan = unionstate->as_whichplan; + nplans = unionstate->as_nplans; + rtentries = node->unionrtentries; + + if (whichplan < 0) + { + /* ---------------- + * if scanning in reverse, we start at + * the last scan in the list and then + * proceed back to the first.. in any case + * we inform ExecProcAppend that we are + * at the end of the line by returning FALSE + * ---------------- + */ + unionstate->as_whichplan = 0; + return FALSE; + + } + else if (whichplan >= nplans) + { + /* ---------------- + * as above, end the scan if we go beyond + * the last scan in our list.. + * ---------------- + */ + unionstate->as_whichplan = nplans - 1; + return FALSE; + + } + else + { + /* ---------------- + * initialize the scan + * (and update the range table appropriately) + * (doesn't this leave the range table hosed for anybody upstream + * of the Append node??? - jolly ) + * ---------------- + */ + if (node->unionrelid > 0) + { + rtentry = nth(whichplan, rtentries); + if (rtentry == NULL) + elog(DEBUG, "exec_append_initialize_next: rtentry is nil"); + + unionrelid = node->unionrelid; + + rt_store(unionrelid, rangeTable, rtentry); + + if (unionstate->as_junkFilter_list) + { + estate->es_junkFilter = + (JunkFilter *) nth(whichplan, + unionstate->as_junkFilter_list); + } + if (unionstate->as_result_relation_info_list) + { + estate->es_result_relation_info = + (RelationInfo *) nth(whichplan, + unionstate->as_result_relation_info_list); + } + result_slot->ttc_whichplan = whichplan; + } + + return TRUE; } - - return TRUE; - } } /* ---------------------------------------------------------------- - * ExecInitAppend - * - * Begins all of the subscans of the append node, storing the - * scan structures in the 'initialized' vector of the append-state - * structure. + * ExecInitAppend + * + * Begins all of the subscans of the append node, storing the + * scan structures in the 'initialized' vector of the append-state + * structure. * - * (This is potentially wasteful, since the entire result of the - * append node may not be scanned, but this way all of the - * structures get allocated in the executor's top level memory - * block instead of that of the call to ExecProcAppend.) - * - * Returns the scan result of the first scan. + * (This is potentially wasteful, since the entire result of the + * append node may not be scanned, but this way all of the + * structures get allocated in the executor's top level memory + * block instead of that of the call to ExecProcAppend.) + * + * Returns the scan result of the first scan. * ---------------------------------------------------------------- */ bool -ExecInitAppend(Append *node, EState *estate, Plan *parent) +ExecInitAppend(Append * node, EState * estate, Plan * parent) { - AppendState *unionstate; - int nplans; - List *resultList = NULL; - List *rtentries; - List *unionplans; - bool *initialized; - int i; - Plan *initNode; - List *junkList; - RelationInfo *es_rri = estate->es_result_relation_info; - - /* ---------------- - * assign execution state to node and get information - * for append state - * ---------------- - */ - node->plan.state = estate; - - unionplans = node->unionplans; - nplans = length(unionplans); - rtentries = node->unionrtentries; - - CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext); - initialized = (bool *)palloc(nplans * sizeof(bool)); - - /* ---------------- - * create new AppendState for our append node - * ---------------- - */ - unionstate = makeNode(AppendState); - unionstate->as_whichplan = 0; - unionstate->as_nplans = nplans; - unionstate->as_initialized = initialized; - unionstate->as_rtentries = rtentries; - - node->unionstate = unionstate; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks - * - * Append plans don't have expression contexts because they - * never call ExecQual or ExecTargetList. - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &unionstate->cstate, parent); - + AppendState *unionstate; + int nplans; + List *resultList = NULL; + List *rtentries; + List *unionplans; + bool *initialized; + int i; + Plan *initNode; + List *junkList; + RelationInfo *es_rri = estate->es_result_relation_info; + + /* ---------------- + * assign execution state to node and get information + * for append state + * ---------------- + */ + node->plan.state = estate; + + unionplans = node->unionplans; + nplans = length(unionplans); + rtentries = node->unionrtentries; + + CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext); + initialized = (bool *) palloc(nplans * sizeof(bool)); + + /* ---------------- + * create new AppendState for our append node + * ---------------- + */ + unionstate = makeNode(AppendState); + unionstate->as_whichplan = 0; + unionstate->as_nplans = nplans; + unionstate->as_initialized = initialized; + unionstate->as_rtentries = rtentries; + + node->unionstate = unionstate; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks + * + * Append plans don't have expression contexts because they + * never call ExecQual or ExecTargetList. + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &unionstate->cstate, parent); + #define APPEND_NSLOTS 1 - /* ---------------- - * append nodes still have Result slots, which hold pointers - * to tuples, so we have to initialize them.. - * ---------------- - */ - ExecInitResultTupleSlot(estate, &unionstate->cstate); - - /* - * If the inherits rtentry is the result relation, we have to make - * a result relation info list for all inheritors so we can update - * their indices and put the result tuples in the right place etc. - * - * e.g. replace p (age = p.age + 1) from p in person* - */ - if ((es_rri != (RelationInfo*)NULL) && - (node->unionrelid == es_rri->ri_RangeTableIndex)) + /* ---------------- + * append nodes still have Result slots, which hold pointers + * to tuples, so we have to initialize them.. + * ---------------- + */ + ExecInitResultTupleSlot(estate, &unionstate->cstate); + + /* + * If the inherits rtentry is the result relation, we have to make a + * result relation info list for all inheritors so we can update their + * indices and put the result tuples in the right place etc. + * + * e.g. replace p (age = p.age + 1) from p in person* + */ + if ((es_rri != (RelationInfo *) NULL) && + (node->unionrelid == es_rri->ri_RangeTableIndex)) { - RelationInfo *rri; - List *rtentryP; - - foreach(rtentryP,rtentries) + RelationInfo *rri; + List *rtentryP; + + foreach(rtentryP, rtentries) { - Oid reloid; - RangeTblEntry *rtentry = lfirst(rtentryP); - - reloid = rtentry->relid; - rri = makeNode(RelationInfo); - rri->ri_RangeTableIndex = es_rri->ri_RangeTableIndex; - rri->ri_RelationDesc = heap_open(reloid); - rri->ri_NumIndices = 0; - rri->ri_IndexRelationDescs = NULL; /* index descs */ - rri->ri_IndexRelationInfo = NULL; /* index key info */ - - resultList = lcons(rri,resultList); - ExecOpenIndices(reloid, rri); + Oid reloid; + RangeTblEntry *rtentry = lfirst(rtentryP); + + reloid = rtentry->relid; + rri = makeNode(RelationInfo); + rri->ri_RangeTableIndex = es_rri->ri_RangeTableIndex; + rri->ri_RelationDesc = heap_open(reloid); + rri->ri_NumIndices = 0; + rri->ri_IndexRelationDescs = NULL; /* index descs */ + rri->ri_IndexRelationInfo = NULL; /* index key info */ + + resultList = lcons(rri, resultList); + ExecOpenIndices(reloid, rri); } - unionstate->as_result_relation_info_list = resultList; + unionstate->as_result_relation_info_list = resultList; } - /* ---------------- - * call ExecInitNode on each of the plans in our list - * and save the results into the array "initialized" - * ---------------- - */ - junkList = NIL; - - for(i = 0; i < nplans ; i++ ) { - JunkFilter *j; - List *targetList; - /* ---------------- - * NOTE: we first modify range table in - * exec_append_initialize_next() and - * then initialize the subnode, - * since it may use the range table. - * ---------------- - */ - unionstate->as_whichplan = i; - exec_append_initialize_next(node); - - initNode = (Plan *) nth(i, unionplans); - initialized[i] = ExecInitNode(initNode, estate, (Plan*) node); - - /* --------------- - * Each targetlist in the subplan may need its own junk filter - * - * This is true only when the reln being replaced/deleted is - * the one that we're looking at the subclasses of - * --------------- + /* ---------------- + * call ExecInitNode on each of the plans in our list + * and save the results into the array "initialized" + * ---------------- */ - if ((es_rri != (RelationInfo*)NULL) && - (node->unionrelid == es_rri->ri_RangeTableIndex)) { - - targetList = initNode->targetlist; - j = (JunkFilter *) ExecInitJunkFilter(targetList); - junkList = lappend(junkList, j); + junkList = NIL; + + for (i = 0; i < nplans; i++) + { + JunkFilter *j; + List *targetList; + + /* ---------------- + * NOTE: we first modify range table in + * exec_append_initialize_next() and + * then initialize the subnode, + * since it may use the range table. + * ---------------- + */ + unionstate->as_whichplan = i; + exec_append_initialize_next(node); + + initNode = (Plan *) nth(i, unionplans); + initialized[i] = ExecInitNode(initNode, estate, (Plan *) node); + + /* --------------- + * Each targetlist in the subplan may need its own junk filter + * + * This is true only when the reln being replaced/deleted is + * the one that we're looking at the subclasses of + * --------------- + */ + if ((es_rri != (RelationInfo *) NULL) && + (node->unionrelid == es_rri->ri_RangeTableIndex)) + { + + targetList = initNode->targetlist; + j = (JunkFilter *) ExecInitJunkFilter(targetList); + junkList = lappend(junkList, j); + } + } - - } - unionstate->as_junkFilter_list = junkList; - if (junkList != NIL) - estate->es_junkFilter = (JunkFilter *)lfirst(junkList); - - /* ---------------- - * initialize the return type from the appropriate subplan. - * ---------------- - */ - initNode = (Plan *) nth(0, unionplans); - ExecAssignResultType(&unionstate->cstate, -/* ExecGetExecTupDesc(initNode), */ - ExecGetTupType(initNode)); - unionstate->cstate.cs_ProjInfo = NULL; - - /* ---------------- - * return the result from the first subplan's initialization - * ---------------- - */ - unionstate->as_whichplan = 0; - exec_append_initialize_next(node); + unionstate->as_junkFilter_list = junkList; + if (junkList != NIL) + estate->es_junkFilter = (JunkFilter *) lfirst(junkList); + + /* ---------------- + * initialize the return type from the appropriate subplan. + * ---------------- + */ + initNode = (Plan *) nth(0, unionplans); + ExecAssignResultType(&unionstate->cstate, +/* ExecGetExecTupDesc(initNode), */ + ExecGetTupType(initNode)); + unionstate->cstate.cs_ProjInfo = NULL; + + /* ---------------- + * return the result from the first subplan's initialization + * ---------------- + */ + unionstate->as_whichplan = 0; + exec_append_initialize_next(node); #if 0 - result = (List *) initialized[0]; -#endif - return TRUE; + result = (List *) initialized[0]; +#endif + return TRUE; } int -ExecCountSlotsAppend(Append *node) +ExecCountSlotsAppend(Append * node) { - List *plan; - List *unionplans = node->unionplans; - int nSlots = 0; - - foreach (plan,unionplans) { - nSlots += ExecCountSlotsNode((Plan *)lfirst(plan)); - } - return nSlots + APPEND_NSLOTS; + List *plan; + List *unionplans = node->unionplans; + int nSlots = 0; + + foreach(plan, unionplans) + { + nSlots += ExecCountSlotsNode((Plan *) lfirst(plan)); + } + return nSlots + APPEND_NSLOTS; } /* ---------------------------------------------------------------- - * ExecProcAppend - * - * Handles the iteration over the multiple scans. - * - * NOTE: Can't call this ExecAppend, that name is used in execMain.l + * ExecProcAppend + * + * Handles the iteration over the multiple scans. + * + * NOTE: Can't call this ExecAppend, that name is used in execMain.l * ---------------------------------------------------------------- */ TupleTableSlot * -ExecProcAppend(Append *node) +ExecProcAppend(Append * node) { - EState *estate; - AppendState *unionstate; - - int whichplan; - List *unionplans; - Plan *subnode; - TupleTableSlot *result; - TupleTableSlot *result_slot; - ScanDirection direction; - - /* ---------------- - * get information from the node - * ---------------- - */ - unionstate = node->unionstate; - estate = node->plan.state; - direction = estate->es_direction; - - unionplans = node->unionplans; - whichplan = unionstate->as_whichplan; - result_slot = unionstate->cstate.cs_ResultTupleSlot; - - /* ---------------- - * figure out which subplan we are currently processing - * ---------------- - */ - subnode = (Plan *) nth(whichplan, unionplans); - - if (subnode == NULL) - elog(DEBUG, "ExecProcAppend: subnode is NULL"); - - /* ---------------- - * get a tuple from the subplan - * ---------------- - */ - result = ExecProcNode(subnode, (Plan*)node); - - if (! TupIsNull(result)) { - /* ---------------- - * if the subplan gave us something then place a copy of - * whatever we get into our result slot and return it, else.. - * ---------------- - */ - return ExecStoreTuple(result->val, - result_slot, result->ttc_buffer, false); - - } else { - /* ---------------- - * .. go on to the "next" subplan in the appropriate - * direction and try processing again (recursively) - * ---------------- - */ - whichplan = unionstate->as_whichplan; - - if (ScanDirectionIsForward(direction)) - { - unionstate->as_whichplan = whichplan + 1; - } - else - { - unionstate->as_whichplan = whichplan - 1; - } - + EState *estate; + AppendState *unionstate; + + int whichplan; + List *unionplans; + Plan *subnode; + TupleTableSlot *result; + TupleTableSlot *result_slot; + ScanDirection direction; + /* ---------------- - * return something from next node or an empty slot - * all of our subplans have been exhausted. + * get information from the node * ---------------- */ - if (exec_append_initialize_next(node)) { - ExecSetSlotDescriptorIsNew(result_slot, true); - return - ExecProcAppend(node); - } else - return ExecClearTuple(result_slot); - } + unionstate = node->unionstate; + estate = node->plan.state; + direction = estate->es_direction; + + unionplans = node->unionplans; + whichplan = unionstate->as_whichplan; + result_slot = unionstate->cstate.cs_ResultTupleSlot; + + /* ---------------- + * figure out which subplan we are currently processing + * ---------------- + */ + subnode = (Plan *) nth(whichplan, unionplans); + + if (subnode == NULL) + elog(DEBUG, "ExecProcAppend: subnode is NULL"); + + /* ---------------- + * get a tuple from the subplan + * ---------------- + */ + result = ExecProcNode(subnode, (Plan *) node); + + if (!TupIsNull(result)) + { + /* ---------------- + * if the subplan gave us something then place a copy of + * whatever we get into our result slot and return it, else.. + * ---------------- + */ + return ExecStoreTuple(result->val, + result_slot, result->ttc_buffer, false); + + } + else + { + /* ---------------- + * .. go on to the "next" subplan in the appropriate + * direction and try processing again (recursively) + * ---------------- + */ + whichplan = unionstate->as_whichplan; + + if (ScanDirectionIsForward(direction)) + { + unionstate->as_whichplan = whichplan + 1; + } + else + { + unionstate->as_whichplan = whichplan - 1; + } + + /* ---------------- + * return something from next node or an empty slot + * all of our subplans have been exhausted. + * ---------------- + */ + if (exec_append_initialize_next(node)) + { + ExecSetSlotDescriptorIsNew(result_slot, true); + return + ExecProcAppend(node); + } + else + return ExecClearTuple(result_slot); + } } /* ---------------------------------------------------------------- - * ExecEndAppend - * - * Shuts down the subscans of the append node. - * - * Returns nothing of interest. + * ExecEndAppend + * + * Shuts down the subscans of the append node. + * + * Returns nothing of interest. * ---------------------------------------------------------------- */ void -ExecEndAppend(Append *node) +ExecEndAppend(Append * node) { - AppendState *unionstate; - int nplans; - List *unionplans; - bool *initialized; - int i; - List *resultRelationInfoList; - RelationInfo *resultRelationInfo; - - /* ---------------- - * get information from the node - * ---------------- - */ - unionstate = node->unionstate; - unionplans = node->unionplans; - nplans = unionstate->as_nplans; - initialized = unionstate->as_initialized; - - /* ---------------- - * shut down each of the subscans - * ---------------- - */ - for(i = 0; i < nplans; i++) { - if (initialized[i]==TRUE) { - ExecEndNode( (Plan *) nth(i, unionplans), (Plan*)node ); - } - } - - /* ---------------- - * close out the different result relations - * ---------------- - */ - resultRelationInfoList = unionstate->as_result_relation_info_list; - while (resultRelationInfoList != NIL) { - Relation resultRelationDesc; - - resultRelationInfo = (RelationInfo*) lfirst(resultRelationInfoList); - resultRelationDesc = resultRelationInfo->ri_RelationDesc; - heap_close(resultRelationDesc); - pfree(resultRelationInfo); - resultRelationInfoList = lnext(resultRelationInfoList); - } - if (unionstate->as_result_relation_info_list) - pfree(unionstate->as_result_relation_info_list); - - /* XXX should free unionstate->as_rtentries and unionstate->as_junkfilter_list here */ -} + AppendState *unionstate; + int nplans; + List *unionplans; + bool *initialized; + int i; + List *resultRelationInfoList; + RelationInfo *resultRelationInfo; + + /* ---------------- + * get information from the node + * ---------------- + */ + unionstate = node->unionstate; + unionplans = node->unionplans; + nplans = unionstate->as_nplans; + initialized = unionstate->as_initialized; + + /* ---------------- + * shut down each of the subscans + * ---------------- + */ + for (i = 0; i < nplans; i++) + { + if (initialized[i] == TRUE) + { + ExecEndNode((Plan *) nth(i, unionplans), (Plan *) node); + } + } + + /* ---------------- + * close out the different result relations + * ---------------- + */ + resultRelationInfoList = unionstate->as_result_relation_info_list; + while (resultRelationInfoList != NIL) + { + Relation resultRelationDesc; + resultRelationInfo = (RelationInfo *) lfirst(resultRelationInfoList); + resultRelationDesc = resultRelationInfo->ri_RelationDesc; + heap_close(resultRelationDesc); + pfree(resultRelationInfo); + resultRelationInfoList = lnext(resultRelationInfoList); + } + if (unionstate->as_result_relation_info_list) + pfree(unionstate->as_result_relation_info_list); + + /* + * XXX should free unionstate->as_rtentries and + * unionstate->as_junkfilter_list here + */ +} diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index 0637a8dd282..1a96a1ee911 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -1,19 +1,19 @@ /*------------------------------------------------------------------------- * * nodeGroup.c-- - * Routines to handle group nodes (used for queries with GROUP BY clause). + * Routines to handle group nodes (used for queries with GROUP BY clause). * * Copyright (c) 1994, Regents of the University of California * * * DESCRIPTION - * The Group node is designed for handling queries with a GROUP BY clause. - * It's outer plan must be a sort node. It assumes that the tuples it gets - * back from the outer plan is sorted in the order specified by the group - * columns. (ie. tuples from the same group are consecutive) + * The Group node is designed for handling queries with a GROUP BY clause. + * It's outer plan must be a sort node. It assumes that the tuples it gets + * back from the outer plan is sorted in the order specified by the group + * columns. (ie. tuples from the same group are consecutive) * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.5 1997/01/10 20:17:35 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.6 1997/09/07 04:41:31 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -28,329 +28,348 @@ #include "executor/executor.h" #include "executor/nodeGroup.h" -static TupleTableSlot *ExecGroupEveryTuple(Group *node); -static TupleTableSlot *ExecGroupOneTuple(Group *node); -static bool sameGroup(TupleTableSlot *oldslot, TupleTableSlot *newslot, - int numCols, AttrNumber *grpColIdx, TupleDesc tupdesc); +static TupleTableSlot *ExecGroupEveryTuple(Group * node); +static TupleTableSlot *ExecGroupOneTuple(Group * node); +static bool +sameGroup(TupleTableSlot * oldslot, TupleTableSlot * newslot, + int numCols, AttrNumber * grpColIdx, TupleDesc tupdesc); /* --------------------------------------- - * ExecGroup - + * ExecGroup - * - * There are two modes in which tuples are returned by ExecGroup. If - * tuplePerGroup is TRUE, every tuple from the same group will be - * returned, followed by a NULL at the end of each group. This is - * useful for Agg node which needs to aggregate over tuples of the same - * group. (eg. SELECT salary, count{*} FROM emp GROUP BY salary) + * There are two modes in which tuples are returned by ExecGroup. If + * tuplePerGroup is TRUE, every tuple from the same group will be + * returned, followed by a NULL at the end of each group. This is + * useful for Agg node which needs to aggregate over tuples of the same + * group. (eg. SELECT salary, count{*} FROM emp GROUP BY salary) * - * If tuplePerGroup is FALSE, only one tuple per group is returned. The - * tuple returned contains only the group columns. NULL is returned only - * at the end when no more groups is present. This is useful when - * the query does not involve aggregates. (eg. SELECT salary FROM emp - * GROUP BY salary) + * If tuplePerGroup is FALSE, only one tuple per group is returned. The + * tuple returned contains only the group columns. NULL is returned only + * at the end when no more groups is present. This is useful when + * the query does not involve aggregates. (eg. SELECT salary FROM emp + * GROUP BY salary) * ------------------------------------------ */ TupleTableSlot * -ExecGroup(Group *node) +ExecGroup(Group * node) { - if (node->tuplePerGroup) - return ExecGroupEveryTuple(node); - else - return ExecGroupOneTuple(node); + if (node->tuplePerGroup) + return ExecGroupEveryTuple(node); + else + return ExecGroupOneTuple(node); } /* * ExecGroupEveryTuple - - * return every tuple with a NULL between each group + * return every tuple with a NULL between each group */ static TupleTableSlot * -ExecGroupEveryTuple(Group *node) +ExecGroupEveryTuple(Group * node) { - GroupState *grpstate; - EState *estate; - ExprContext *econtext; + GroupState *grpstate; + EState *estate; + ExprContext *econtext; - HeapTuple outerTuple = NULL; - TupleTableSlot *outerslot, *lastslot; - ProjectionInfo *projInfo; - TupleTableSlot *resultSlot; + HeapTuple outerTuple = NULL; + TupleTableSlot *outerslot, + *lastslot; + ProjectionInfo *projInfo; + TupleTableSlot *resultSlot; - bool isDone; + bool isDone; - /* --------------------- - * get state info from node - * --------------------- - */ - grpstate = node->grpstate; - if (grpstate->grp_done) - return NULL; + /* --------------------- + * get state info from node + * --------------------- + */ + grpstate = node->grpstate; + if (grpstate->grp_done) + return NULL; - estate = node->plan.state; + estate = node->plan.state; - econtext = grpstate->csstate.cstate.cs_ExprContext; + econtext = grpstate->csstate.cstate.cs_ExprContext; - if (grpstate->grp_useLastTuple) { - /* - * we haven't returned last tuple yet because it is not of the - * same group - */ - grpstate->grp_useLastTuple = FALSE; + if (grpstate->grp_useLastTuple) + { + + /* + * we haven't returned last tuple yet because it is not of the + * same group + */ + grpstate->grp_useLastTuple = FALSE; - ExecStoreTuple(grpstate->grp_lastSlot->val, - grpstate->csstate.css_ScanTupleSlot, - grpstate->grp_lastSlot->ttc_buffer, - false); - } else { - outerslot = ExecProcNode(outerPlan(node), (Plan*)node); - if (outerslot) - outerTuple = outerslot->val; - if (!HeapTupleIsValid(outerTuple)) { - grpstate->grp_done = TRUE; - return NULL; + ExecStoreTuple(grpstate->grp_lastSlot->val, + grpstate->csstate.css_ScanTupleSlot, + grpstate->grp_lastSlot->ttc_buffer, + false); + } + else + { + outerslot = ExecProcNode(outerPlan(node), (Plan *) node); + if (outerslot) + outerTuple = outerslot->val; + if (!HeapTupleIsValid(outerTuple)) + { + grpstate->grp_done = TRUE; + return NULL; + } + + /* ---------------- + * Compare with last tuple and see if this tuple is of + * the same group. + * ---------------- + */ + lastslot = grpstate->csstate.css_ScanTupleSlot; + + if (lastslot->val != NULL && + (!sameGroup(lastslot, outerslot, + node->numCols, node->grpColIdx, + ExecGetScanType(&grpstate->csstate)))) + { +/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/ + + grpstate->grp_useLastTuple = TRUE; + + /* save it for next time */ + grpstate->grp_lastSlot = outerslot; + + /* + * signifies the end of the group + */ + return NULL; + } + + ExecStoreTuple(outerTuple, + grpstate->csstate.css_ScanTupleSlot, + outerslot->ttc_buffer, + false); } /* ---------------- - * Compare with last tuple and see if this tuple is of - * the same group. + * form a projection tuple, store it in the result tuple + * slot and return it. * ---------------- */ - lastslot = grpstate->csstate.css_ScanTupleSlot; - - if (lastslot->val != NULL && - (!sameGroup(lastslot, outerslot, - node->numCols, node->grpColIdx, - ExecGetScanType(&grpstate->csstate)))) { -/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/ - - grpstate->grp_useLastTuple = TRUE; + projInfo = grpstate->csstate.cstate.cs_ProjInfo; - /* save it for next time */ - grpstate->grp_lastSlot = outerslot; + econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot; + resultSlot = ExecProject(projInfo, &isDone); - /* - * signifies the end of the group - */ - return NULL; - } - - ExecStoreTuple(outerTuple, - grpstate->csstate.css_ScanTupleSlot, - outerslot->ttc_buffer, - false); - } - - /* ---------------- - * form a projection tuple, store it in the result tuple - * slot and return it. - * ---------------- - */ - projInfo = grpstate->csstate.cstate.cs_ProjInfo; - - econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot; - resultSlot = ExecProject(projInfo, &isDone); - - return resultSlot; + return resultSlot; } /* * ExecGroupOneTuple - - * returns one tuple per group, a NULL at the end when there are no more - * tuples. + * returns one tuple per group, a NULL at the end when there are no more + * tuples. */ static TupleTableSlot * -ExecGroupOneTuple(Group *node) +ExecGroupOneTuple(Group * node) { - GroupState *grpstate; - EState *estate; - ExprContext *econtext; + GroupState *grpstate; + EState *estate; + ExprContext *econtext; - HeapTuple outerTuple = NULL; - TupleTableSlot *outerslot, *lastslot; - ProjectionInfo *projInfo; - TupleTableSlot *resultSlot; + HeapTuple outerTuple = NULL; + TupleTableSlot *outerslot, + *lastslot; + ProjectionInfo *projInfo; + TupleTableSlot *resultSlot; - bool isDone; + bool isDone; - /* --------------------- - * get state info from node - * --------------------- - */ - grpstate = node->grpstate; - if (grpstate->grp_done) - return NULL; + /* --------------------- + * get state info from node + * --------------------- + */ + grpstate = node->grpstate; + if (grpstate->grp_done) + return NULL; - estate = node->plan.state; + estate = node->plan.state; - econtext = node->grpstate->csstate.cstate.cs_ExprContext; + econtext = node->grpstate->csstate.cstate.cs_ExprContext; - if (grpstate->grp_useLastTuple) { - grpstate->grp_useLastTuple = FALSE; - ExecStoreTuple(grpstate->grp_lastSlot->val, - grpstate->csstate.css_ScanTupleSlot, - grpstate->grp_lastSlot->ttc_buffer, - false); - } else { - outerslot = ExecProcNode(outerPlan(node), (Plan*)node); - if (outerslot) outerTuple = outerslot->val; - if (!HeapTupleIsValid(outerTuple)) { - grpstate->grp_done = TRUE; - return NULL; + if (grpstate->grp_useLastTuple) + { + grpstate->grp_useLastTuple = FALSE; + ExecStoreTuple(grpstate->grp_lastSlot->val, + grpstate->csstate.css_ScanTupleSlot, + grpstate->grp_lastSlot->ttc_buffer, + false); } - ExecStoreTuple(outerTuple, - grpstate->csstate.css_ScanTupleSlot, - outerslot->ttc_buffer, - false); - } - lastslot = grpstate->csstate.css_ScanTupleSlot; - - /* - * find all tuples that belong to a group - */ - for(;;) { - outerslot = ExecProcNode(outerPlan(node), (Plan*)node); - outerTuple = (outerslot) ? outerslot->val : NULL; - if (!HeapTupleIsValid(outerTuple)) { - /* - * we have at least one tuple (lastslot) if we reach here - */ - grpstate->grp_done = TRUE; - - /* return lastslot */ - break; + else + { + outerslot = ExecProcNode(outerPlan(node), (Plan *) node); + if (outerslot) + outerTuple = outerslot->val; + if (!HeapTupleIsValid(outerTuple)) + { + grpstate->grp_done = TRUE; + return NULL; + } + ExecStoreTuple(outerTuple, + grpstate->csstate.css_ScanTupleSlot, + outerslot->ttc_buffer, + false); } + lastslot = grpstate->csstate.css_ScanTupleSlot; - /* ---------------- - * Compare with last tuple and see if this tuple is of - * the same group. - * ---------------- + /* + * find all tuples that belong to a group */ - if ((!sameGroup(lastslot, outerslot, - node->numCols, node->grpColIdx, - ExecGetScanType(&grpstate->csstate)))) { -/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/ + for (;;) + { + outerslot = ExecProcNode(outerPlan(node), (Plan *) node); + outerTuple = (outerslot) ? outerslot->val : NULL; + if (!HeapTupleIsValid(outerTuple)) + { + + /* + * we have at least one tuple (lastslot) if we reach here + */ + grpstate->grp_done = TRUE; + + /* return lastslot */ + break; + } + + /* ---------------- + * Compare with last tuple and see if this tuple is of + * the same group. + * ---------------- + */ + if ((!sameGroup(lastslot, outerslot, + node->numCols, node->grpColIdx, + ExecGetScanType(&grpstate->csstate)))) + { +/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/ + + grpstate->grp_useLastTuple = TRUE; + + /* save it for next time */ + grpstate->grp_lastSlot = outerslot; + + /* return lastslot */ + break; + } + + ExecStoreTuple(outerTuple, + grpstate->csstate.css_ScanTupleSlot, + outerslot->ttc_buffer, + false); + + lastslot = grpstate->csstate.css_ScanTupleSlot; + } - grpstate->grp_useLastTuple = TRUE; + ExecStoreTuple(lastslot->val, + grpstate->csstate.css_ScanTupleSlot, + lastslot->ttc_buffer, + false); - /* save it for next time */ - grpstate->grp_lastSlot = outerslot; + /* ---------------- + * form a projection tuple, store it in the result tuple + * slot and return it. + * ---------------- + */ + projInfo = grpstate->csstate.cstate.cs_ProjInfo; - /* return lastslot */ - break; - } - - ExecStoreTuple(outerTuple, - grpstate->csstate.css_ScanTupleSlot, - outerslot->ttc_buffer, - false); + econtext->ecxt_scantuple = lastslot; + resultSlot = ExecProject(projInfo, &isDone); - lastslot = grpstate->csstate.css_ScanTupleSlot; - } - - ExecStoreTuple(lastslot->val, - grpstate->csstate.css_ScanTupleSlot, - lastslot->ttc_buffer, - false); - - /* ---------------- - * form a projection tuple, store it in the result tuple - * slot and return it. - * ---------------- - */ - projInfo = grpstate->csstate.cstate.cs_ProjInfo; - - econtext->ecxt_scantuple = lastslot; - resultSlot = ExecProject(projInfo, &isDone); - - return resultSlot; + return resultSlot; } /* ----------------- - * ExecInitGroup + * ExecInitGroup * - * Creates the run-time information for the group node produced by the - * planner and initializes its outer subtree + * Creates the run-time information for the group node produced by the + * planner and initializes its outer subtree * ----------------- */ bool -ExecInitGroup(Group *node, EState *estate, Plan *parent) +ExecInitGroup(Group * node, EState * estate, Plan * parent) { - GroupState *grpstate; - Plan *outerPlan; - - /* - * assign the node's execution state - */ - node->plan.state = estate; - - /* - * create state structure - */ - grpstate = makeNode(GroupState); - node->grpstate = grpstate; - grpstate->grp_useLastTuple = FALSE; - grpstate->grp_done = FALSE; - - /* - * assign node's base id and create expression context - */ - ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate, - (Plan*) parent); - ExecAssignExprContext(estate, &grpstate->csstate.cstate); - + GroupState *grpstate; + Plan *outerPlan; + + /* + * assign the node's execution state + */ + node->plan.state = estate; + + /* + * create state structure + */ + grpstate = makeNode(GroupState); + node->grpstate = grpstate; + grpstate->grp_useLastTuple = FALSE; + grpstate->grp_done = FALSE; + + /* + * assign node's base id and create expression context + */ + ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate, + (Plan *) parent); + ExecAssignExprContext(estate, &grpstate->csstate.cstate); + #define GROUP_NSLOTS 2 - /* - * tuple table initialization - */ - ExecInitScanTupleSlot(estate, &grpstate->csstate); - ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate); - - /* - * initializes child nodes - */ - outerPlan = outerPlan(node); - ExecInitNode(outerPlan, estate, (Plan *)node); - - /* ---------------- - * initialize tuple type. - * ---------------- - */ - ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate); - - /* - * Initialize tuple type for both result and scan. - * This node does no projection - */ - ExecAssignResultTypeFromTL((Plan*) node, &grpstate->csstate.cstate); - ExecAssignProjectionInfo((Plan*)node, &grpstate->csstate.cstate); - - return TRUE; + + /* + * tuple table initialization + */ + ExecInitScanTupleSlot(estate, &grpstate->csstate); + ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate); + + /* + * initializes child nodes + */ + outerPlan = outerPlan(node); + ExecInitNode(outerPlan, estate, (Plan *) node); + + /* ---------------- + * initialize tuple type. + * ---------------- + */ + ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate); + + /* + * Initialize tuple type for both result and scan. This node does no + * projection + */ + ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate); + ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate); + + return TRUE; } int -ExecCountSlotsGroup(Group *node) +ExecCountSlotsGroup(Group * node) { - return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS; + return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS; } /* ------------------------ - * ExecEndGroup(node) + * ExecEndGroup(node) * * ----------------------- */ void -ExecEndGroup(Group *node) +ExecEndGroup(Group * node) { - GroupState *grpstate; - Plan *outerPlan; + GroupState *grpstate; + Plan *outerPlan; + + grpstate = node->grpstate; - grpstate = node->grpstate; + ExecFreeProjectionInfo(&grpstate->csstate.cstate); - ExecFreeProjectionInfo(&grpstate->csstate.cstate); + outerPlan = outerPlan(node); + ExecEndNode(outerPlan, (Plan *) node); - outerPlan = outerPlan(node); - ExecEndNode(outerPlan, (Plan*)node); - - /* clean up tuple table */ - ExecClearTuple(grpstate->csstate.css_ScanTupleSlot); + /* clean up tuple table */ + ExecClearTuple(grpstate->csstate.css_ScanTupleSlot); } /***************************************************************************** @@ -360,54 +379,63 @@ ExecEndGroup(Group *node) /* * code swiped from nodeUnique.c */ -static bool -sameGroup(TupleTableSlot *oldslot, - TupleTableSlot *newslot, - int numCols, - AttrNumber *grpColIdx, - TupleDesc tupdesc) +static bool +sameGroup(TupleTableSlot * oldslot, + TupleTableSlot * newslot, + int numCols, + AttrNumber * grpColIdx, + TupleDesc tupdesc) { - bool isNull1,isNull2; - char *attr1, *attr2; - char *val1, *val2; - int i; - AttrNumber att; - Oid typoutput; - - for(i = 0; i < numCols; i++) { - att = grpColIdx[i]; - typoutput = typtoout((Oid)tupdesc->attrs[att-1]->atttypid); - - attr1 = heap_getattr(oldslot->val, - InvalidBuffer, - att, - tupdesc, - &isNull1); - - attr2 = heap_getattr(newslot->val, - InvalidBuffer, - att, - tupdesc, - &isNull2); - - if (isNull1 == isNull2) { - if (isNull1) /* both are null, they are equal */ - continue; - - val1 = fmgr(typoutput, attr1, - gettypelem(tupdesc->attrs[att-1]->atttypid)); - val2 = fmgr(typoutput, attr2, - gettypelem(tupdesc->attrs[att-1]->atttypid)); - - /* now, val1 and val2 are ascii representations so we can - use strcmp for comparison */ - if (strcmp(val1,val2) != 0) - return FALSE; - } else { - /* one is null and the other isn't, they aren't equal */ - return FALSE; + bool isNull1, + isNull2; + char *attr1, + *attr2; + char *val1, + *val2; + int i; + AttrNumber att; + Oid typoutput; + + for (i = 0; i < numCols; i++) + { + att = grpColIdx[i]; + typoutput = typtoout((Oid) tupdesc->attrs[att - 1]->atttypid); + + attr1 = heap_getattr(oldslot->val, + InvalidBuffer, + att, + tupdesc, + &isNull1); + + attr2 = heap_getattr(newslot->val, + InvalidBuffer, + att, + tupdesc, + &isNull2); + + if (isNull1 == isNull2) + { + if (isNull1) /* both are null, they are equal */ + continue; + + val1 = fmgr(typoutput, attr1, + gettypelem(tupdesc->attrs[att - 1]->atttypid)); + val2 = fmgr(typoutput, attr2, + gettypelem(tupdesc->attrs[att - 1]->atttypid)); + + /* + * now, val1 and val2 are ascii representations so we can use + * strcmp for comparison + */ + if (strcmp(val1, val2) != 0) + return FALSE; + } + else + { + /* one is null and the other isn't, they aren't equal */ + return FALSE; + } } - } - return TRUE; + return TRUE; } diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 10bfe9842cf..b25939fa832 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -1,26 +1,26 @@ /*------------------------------------------------------------------------- * * nodeHash.c-- - * Routines to hash relations for hashjoin + * Routines to hash relations for hashjoin * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.10 1997/08/19 21:31:08 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.11 1997/09/07 04:41:32 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * ExecHash - generate an in-memory hash table of the relation - * ExecInitHash - initialize node and subnodes.. - * ExecEndHash - shutdown node and subnodes + * ExecHash - generate an in-memory hash table of the relation + * ExecInitHash - initialize node and subnodes.. + * ExecEndHash - shutdown node and subnodes * */ #include <sys/types.h> -#include <stdio.h> /* for sprintf() */ +#include <stdio.h> /* for sprintf() */ #include <math.h> #include <string.h> #include <sys/file.h> @@ -32,9 +32,9 @@ #include "postgres.h" -#include "storage/fd.h" /* for SEEK_ */ +#include "storage/fd.h" /* for SEEK_ */ #include "storage/ipc.h" -#include "storage/bufmgr.h" /* for BLCKSZ */ +#include "storage/bufmgr.h" /* for BLCKSZ */ #include "executor/executor.h" #include "executor/nodeHash.h" #include "executor/nodeHashjoin.h" @@ -42,827 +42,855 @@ #include "utils/palloc.h" #include "utils/hsearch.h" -extern int NBuffers; -static int HashTBSize; +extern int NBuffers; +static int HashTBSize; -static void mk_hj_temp(char *tempname); -static int hashFunc(char *key, int len); -static int ExecHashPartition(Hash *node); +static void mk_hj_temp(char *tempname); +static int hashFunc(char *key, int len); +static int ExecHashPartition(Hash * node); static RelativeAddr hashTableAlloc(int size, HashJoinTable hashtable); -static void ExecHashOverflowInsert(HashJoinTable hashtable, - HashBucket bucket, - HeapTuple heapTuple); +static void +ExecHashOverflowInsert(HashJoinTable hashtable, + HashBucket bucket, + HeapTuple heapTuple); /* ---------------------------------------------------------------- - * ExecHash + * ExecHash * - * build hash table for hashjoin, all do partitioning if more - * than one batches are required. + * build hash table for hashjoin, all do partitioning if more + * than one batches are required. * ---------------------------------------------------------------- */ TupleTableSlot * -ExecHash(Hash *node) +ExecHash(Hash * node) { - EState *estate; - HashState *hashstate; - Plan *outerNode; - Var *hashkey; - HashJoinTable hashtable; - TupleTableSlot *slot; - ExprContext *econtext; - - int nbatch; - File *batches = NULL; - RelativeAddr *batchPos; - int *batchSizes; - int i; - RelativeAddr *innerbatchNames; - - /* ---------------- - * get state info from node - * ---------------- - */ - - hashstate = node->hashstate; - estate = node->plan.state; - outerNode = outerPlan(node); - - hashtable = node->hashtable; - if (hashtable == NULL) - elog(WARN, "ExecHash: hash table is NULL."); - - nbatch = hashtable->nbatch; - - if (nbatch > 0) { /* if needs hash partition */ - innerbatchNames = (RelativeAddr *) ABSADDR(hashtable->innerbatchNames); - - /* -------------- - * allocate space for the file descriptors of batch files - * then open the batch files in the current processes. - * -------------- - */ - batches = (File*)palloc(nbatch * sizeof(File)); - for (i=0; i<nbatch; i++) { - batches[i] = FileNameOpenFile(ABSADDR(innerbatchNames[i]), - O_CREAT | O_RDWR, 0600); + EState *estate; + HashState *hashstate; + Plan *outerNode; + Var *hashkey; + HashJoinTable hashtable; + TupleTableSlot *slot; + ExprContext *econtext; + + int nbatch; + File *batches = NULL; + RelativeAddr *batchPos; + int *batchSizes; + int i; + RelativeAddr *innerbatchNames; + + /* ---------------- + * get state info from node + * ---------------- + */ + + hashstate = node->hashstate; + estate = node->plan.state; + outerNode = outerPlan(node); + + hashtable = node->hashtable; + if (hashtable == NULL) + elog(WARN, "ExecHash: hash table is NULL."); + + nbatch = hashtable->nbatch; + + if (nbatch > 0) + { /* if needs hash partition */ + innerbatchNames = (RelativeAddr *) ABSADDR(hashtable->innerbatchNames); + + /* -------------- + * allocate space for the file descriptors of batch files + * then open the batch files in the current processes. + * -------------- + */ + batches = (File *) palloc(nbatch * sizeof(File)); + for (i = 0; i < nbatch; i++) + { + batches[i] = FileNameOpenFile(ABSADDR(innerbatchNames[i]), + O_CREAT | O_RDWR, 0600); + } + hashstate->hashBatches = batches; + batchPos = (RelativeAddr *) ABSADDR(hashtable->innerbatchPos); + batchSizes = (int *) ABSADDR(hashtable->innerbatchSizes); + } + + /* ---------------- + * set expression context + * ---------------- + */ + hashkey = node->hashkey; + econtext = hashstate->cstate.cs_ExprContext; + + /* ---------------- + * get tuple and insert into the hash table + * ---------------- + */ + for (;;) + { + slot = ExecProcNode(outerNode, (Plan *) node); + if (TupIsNull(slot)) + break; + + econtext->ecxt_innertuple = slot; + ExecHashTableInsert(hashtable, econtext, hashkey, + hashstate->hashBatches); + + ExecClearTuple(slot); + } + + /* + * end of build phase, flush all the last pages of the batches. + */ + for (i = 0; i < nbatch; i++) + { + if (FileSeek(batches[i], 0L, SEEK_END) < 0) + perror("FileSeek"); + if (FileWrite(batches[i], ABSADDR(hashtable->batch) + i * BLCKSZ, BLCKSZ) < 0) + perror("FileWrite"); + NDirectFileWrite++; } - hashstate->hashBatches = batches; - batchPos = (RelativeAddr*) ABSADDR(hashtable->innerbatchPos); - batchSizes = (int*) ABSADDR(hashtable->innerbatchSizes); - } - - /* ---------------- - * set expression context - * ---------------- - */ - hashkey = node->hashkey; - econtext = hashstate->cstate.cs_ExprContext; - - /* ---------------- - * get tuple and insert into the hash table - * ---------------- - */ - for (;;) { - slot = ExecProcNode(outerNode, (Plan*)node); - if (TupIsNull(slot)) - break; - - econtext->ecxt_innertuple = slot; - ExecHashTableInsert(hashtable, econtext, hashkey, - hashstate->hashBatches); - - ExecClearTuple(slot); - } - - /* - * end of build phase, flush all the last pages of the batches. - */ - for (i=0; i<nbatch; i++) { - if (FileSeek(batches[i], 0L, SEEK_END) < 0) - perror("FileSeek"); - if (FileWrite(batches[i],ABSADDR(hashtable->batch)+i*BLCKSZ,BLCKSZ) < 0) - perror("FileWrite"); - NDirectFileWrite++; - } - - /* --------------------- - * Return the slot so that we have the tuple descriptor - * when we need to save/restore them. -Jeff 11 July 1991 - * --------------------- - */ - return slot; + + /* --------------------- + * Return the slot so that we have the tuple descriptor + * when we need to save/restore them. -Jeff 11 July 1991 + * --------------------- + */ + return slot; } /* ---------------------------------------------------------------- - * ExecInitHash + * ExecInitHash * - * Init routine for Hash node + * Init routine for Hash node * ---------------------------------------------------------------- */ bool -ExecInitHash(Hash *node, EState *estate, Plan *parent) +ExecInitHash(Hash * node, EState * estate, Plan * parent) { - HashState *hashstate; - Plan *outerPlan; - - SO1_printf("ExecInitHash: %s\n", - "initializing hash node"); - - /* ---------------- - * assign the node's execution state - * ---------------- - */ - node->plan.state = estate; - - /* ---------------- - * create state structure - * ---------------- - */ - hashstate = makeNode(HashState); - node->hashstate = hashstate; - hashstate->hashBatches = NULL; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks and - * + create expression context for node - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &hashstate->cstate, parent); - ExecAssignExprContext(estate, &hashstate->cstate); - + HashState *hashstate; + Plan *outerPlan; + + SO1_printf("ExecInitHash: %s\n", + "initializing hash node"); + + /* ---------------- + * assign the node's execution state + * ---------------- + */ + node->plan.state = estate; + + /* ---------------- + * create state structure + * ---------------- + */ + hashstate = makeNode(HashState); + node->hashstate = hashstate; + hashstate->hashBatches = NULL; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks and + * + create expression context for node + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &hashstate->cstate, parent); + ExecAssignExprContext(estate, &hashstate->cstate); + #define HASH_NSLOTS 1 - /* ---------------- - * initialize our result slot - * ---------------- - */ - ExecInitResultTupleSlot(estate, &hashstate->cstate); - - /* ---------------- - * initializes child nodes - * ---------------- - */ - outerPlan = outerPlan(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, &hashstate->cstate); - hashstate->cstate.cs_ProjInfo = NULL; - - return TRUE; + /* ---------------- + * initialize our result slot + * ---------------- + */ + ExecInitResultTupleSlot(estate, &hashstate->cstate); + + /* ---------------- + * initializes child nodes + * ---------------- + */ + outerPlan = outerPlan(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, &hashstate->cstate); + hashstate->cstate.cs_ProjInfo = NULL; + + return TRUE; } int -ExecCountSlotsHash(Hash *node) +ExecCountSlotsHash(Hash * node) { - return ExecCountSlotsNode(outerPlan(node)) + + return ExecCountSlotsNode(outerPlan(node)) + ExecCountSlotsNode(innerPlan(node)) + - HASH_NSLOTS; + HASH_NSLOTS; } /* --------------------------------------------------------------- - * ExecEndHash + * ExecEndHash * - * clean up routine for Hash node + * clean up routine for Hash node * ---------------------------------------------------------------- */ void -ExecEndHash(Hash *node) +ExecEndHash(Hash * node) { - HashState *hashstate; - Plan *outerPlan; - File *batches; - - /* ---------------- - * get info from the hash state - * ---------------- - */ - hashstate = node->hashstate; - batches = hashstate->hashBatches; - if (batches != NULL) - pfree(batches); - - /* ---------------- - * free projection info. no need to free result type info - * because that came from the outer plan... - * ---------------- - */ - ExecFreeProjectionInfo(&hashstate->cstate); - - /* ---------------- - * shut down the subplan - * ---------------- - */ - outerPlan = outerPlan(node); - ExecEndNode(outerPlan, (Plan*)node); -} - -static RelativeAddr + HashState *hashstate; + Plan *outerPlan; + File *batches; + + /* ---------------- + * get info from the hash state + * ---------------- + */ + hashstate = node->hashstate; + batches = hashstate->hashBatches; + if (batches != NULL) + pfree(batches); + + /* ---------------- + * free projection info. no need to free result type info + * because that came from the outer plan... + * ---------------- + */ + ExecFreeProjectionInfo(&hashstate->cstate); + + /* ---------------- + * shut down the subplan + * ---------------- + */ + outerPlan = outerPlan(node); + ExecEndNode(outerPlan, (Plan *) node); +} + +static RelativeAddr hashTableAlloc(int size, HashJoinTable hashtable) { - RelativeAddr p; - p = hashtable->top; - hashtable->top += size; - return p; + RelativeAddr p; + + p = hashtable->top; + hashtable->top += size; + return p; } /* ---------------------------------------------------------------- - * ExecHashTableCreate + * ExecHashTableCreate * - * create a hashtable in shared memory for hashjoin. + * create a hashtable in shared memory for hashjoin. * ---------------------------------------------------------------- */ -#define NTUP_PER_BUCKET 10 -#define FUDGE_FAC 1.5 +#define NTUP_PER_BUCKET 10 +#define FUDGE_FAC 1.5 HashJoinTable -ExecHashTableCreate(Hash *node) +ExecHashTableCreate(Hash * node) { - Plan *outerNode; - int nbatch; - int ntuples; - int tupsize; - IpcMemoryId shmid; - HashJoinTable hashtable; - HashBucket bucket; - int nbuckets; - int totalbuckets; - int bucketsize; - int i; - RelativeAddr *outerbatchNames; - RelativeAddr *outerbatchPos; - RelativeAddr *innerbatchNames; - RelativeAddr *innerbatchPos; - int *innerbatchSizes; - RelativeAddr tempname; - - nbatch = -1; - HashTBSize = NBuffers/2; - while (nbatch < 0) { + Plan *outerNode; + int nbatch; + int ntuples; + int tupsize; + IpcMemoryId shmid; + HashJoinTable hashtable; + HashBucket bucket; + int nbuckets; + int totalbuckets; + int bucketsize; + int i; + RelativeAddr *outerbatchNames; + RelativeAddr *outerbatchPos; + RelativeAddr *innerbatchNames; + RelativeAddr *innerbatchPos; + int *innerbatchSizes; + RelativeAddr tempname; + + nbatch = -1; + HashTBSize = NBuffers / 2; + while (nbatch < 0) + { + + /* + * determine number of batches for the hashjoin + */ + HashTBSize *= 2; + nbatch = ExecHashPartition(node); + } + /* ---------------- + * get information about the size of the relation + * ---------------- + */ + outerNode = outerPlan(node); + ntuples = outerNode->plan_size; + if (ntuples <= 0) + ntuples = 1000; /* XXX just a hack */ + tupsize = outerNode->plan_width + sizeof(HeapTupleData); + + /* + * totalbuckets is the total number of hash buckets needed for the + * entire relation + */ + totalbuckets = ceil((double) ntuples / NTUP_PER_BUCKET); + bucketsize = LONGALIGN(NTUP_PER_BUCKET * tupsize + sizeof(*bucket)); + /* - * determine number of batches for the hashjoin - */ - HashTBSize *= 2; - nbatch = ExecHashPartition(node); - } - /* ---------------- - * get information about the size of the relation - * ---------------- - */ - outerNode = outerPlan(node); - ntuples = outerNode->plan_size; - if (ntuples <= 0) - ntuples = 1000; /* XXX just a hack */ - tupsize = outerNode->plan_width + sizeof(HeapTupleData); - - /* - * totalbuckets is the total number of hash buckets needed for - * the entire relation - */ - totalbuckets = ceil((double)ntuples/NTUP_PER_BUCKET); - bucketsize = LONGALIGN (NTUP_PER_BUCKET * tupsize + sizeof(*bucket)); - - /* - * nbuckets is the number of hash buckets for the first pass - * of hybrid hashjoin - */ - nbuckets = (HashTBSize - nbatch) * BLCKSZ / (bucketsize * FUDGE_FAC); - if (totalbuckets < nbuckets) - totalbuckets = nbuckets; - if (nbatch == 0) - nbuckets = totalbuckets; + * nbuckets is the number of hash buckets for the first pass of hybrid + * hashjoin + */ + nbuckets = (HashTBSize - nbatch) * BLCKSZ / (bucketsize * FUDGE_FAC); + if (totalbuckets < nbuckets) + totalbuckets = nbuckets; + if (nbatch == 0) + nbuckets = totalbuckets; #ifdef HJDEBUG - printf("nbatch = %d, totalbuckets = %d, nbuckets = %d\n", nbatch, totalbuckets, nbuckets); + printf("nbatch = %d, totalbuckets = %d, nbuckets = %d\n", nbatch, totalbuckets, nbuckets); #endif - - /* ---------------- - * in non-parallel machines, we don't need to put the hash table - * in the shared memory. We just palloc it. - * ---------------- - */ - hashtable = (HashJoinTable)palloc((HashTBSize+1)*BLCKSZ); - shmid = 0; - - if (hashtable == NULL) { - elog(WARN, "not enough memory for hashjoin."); - } - /* ---------------- - * initialize the hash table header - * ---------------- - */ - hashtable->nbuckets = nbuckets; - hashtable->totalbuckets = totalbuckets; - hashtable->bucketsize = bucketsize; - hashtable->shmid = shmid; - hashtable->top = sizeof(HashTableData); - hashtable->bottom = HashTBSize * BLCKSZ; - /* - * hashtable->readbuf has to be long aligned!!! - */ - hashtable->readbuf = hashtable->bottom; - hashtable->nbatch = nbatch; - hashtable->curbatch = 0; - hashtable->pcount = hashtable->nprocess = 0; - if (nbatch > 0) { - /* --------------- - * allocate and initialize the outer batches - * --------------- - */ - outerbatchNames = (RelativeAddr*)ABSADDR( - hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable)); - outerbatchPos = (RelativeAddr*)ABSADDR( - hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable)); - for (i=0; i<nbatch; i++) { - tempname = hashTableAlloc(12, hashtable); - mk_hj_temp(ABSADDR(tempname)); - outerbatchNames[i] = tempname; - outerbatchPos[i] = -1; + + /* ---------------- + * in non-parallel machines, we don't need to put the hash table + * in the shared memory. We just palloc it. + * ---------------- + */ + hashtable = (HashJoinTable) palloc((HashTBSize + 1) * BLCKSZ); + shmid = 0; + + if (hashtable == NULL) + { + elog(WARN, "not enough memory for hashjoin."); + } + /* ---------------- + * initialize the hash table header + * ---------------- + */ + hashtable->nbuckets = nbuckets; + hashtable->totalbuckets = totalbuckets; + hashtable->bucketsize = bucketsize; + hashtable->shmid = shmid; + hashtable->top = sizeof(HashTableData); + hashtable->bottom = HashTBSize * BLCKSZ; + + /* + * hashtable->readbuf has to be long aligned!!! + */ + hashtable->readbuf = hashtable->bottom; + hashtable->nbatch = nbatch; + hashtable->curbatch = 0; + hashtable->pcount = hashtable->nprocess = 0; + if (nbatch > 0) + { + /* --------------- + * allocate and initialize the outer batches + * --------------- + */ + outerbatchNames = (RelativeAddr *) ABSADDR( + hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable)); + outerbatchPos = (RelativeAddr *) ABSADDR( + hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable)); + for (i = 0; i < nbatch; i++) + { + tempname = hashTableAlloc(12, hashtable); + mk_hj_temp(ABSADDR(tempname)); + outerbatchNames[i] = tempname; + outerbatchPos[i] = -1; + } + hashtable->outerbatchNames = RELADDR(outerbatchNames); + hashtable->outerbatchPos = RELADDR(outerbatchPos); + /* --------------- + * allocate and initialize the inner batches + * --------------- + */ + innerbatchNames = (RelativeAddr *) ABSADDR( + hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable)); + innerbatchPos = (RelativeAddr *) ABSADDR( + hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable)); + innerbatchSizes = (int *) ABSADDR( + hashTableAlloc(nbatch * sizeof(int), hashtable)); + for (i = 0; i < nbatch; i++) + { + tempname = hashTableAlloc(12, hashtable); + mk_hj_temp(ABSADDR(tempname)); + innerbatchNames[i] = tempname; + innerbatchPos[i] = -1; + innerbatchSizes[i] = 0; + } + hashtable->innerbatchNames = RELADDR(innerbatchNames); + hashtable->innerbatchPos = RELADDR(innerbatchPos); + hashtable->innerbatchSizes = RELADDR(innerbatchSizes); } - hashtable->outerbatchNames = RELADDR(outerbatchNames); - hashtable->outerbatchPos = RELADDR(outerbatchPos); - /* --------------- - * allocate and initialize the inner batches - * --------------- - */ - innerbatchNames = (RelativeAddr*)ABSADDR( - hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable)); - innerbatchPos = (RelativeAddr*)ABSADDR( - hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable)); - innerbatchSizes = (int*)ABSADDR( - hashTableAlloc(nbatch * sizeof(int), hashtable)); - for (i=0; i<nbatch; i++) { - tempname = hashTableAlloc(12, hashtable); - mk_hj_temp(ABSADDR(tempname)); - innerbatchNames[i] = tempname; - innerbatchPos[i] = -1; - innerbatchSizes[i] = 0; + else + { + hashtable->outerbatchNames = (RelativeAddr) NULL; + hashtable->outerbatchPos = (RelativeAddr) NULL; + hashtable->innerbatchNames = (RelativeAddr) NULL; + hashtable->innerbatchPos = (RelativeAddr) NULL; + hashtable->innerbatchSizes = (RelativeAddr) NULL; } - hashtable->innerbatchNames = RELADDR(innerbatchNames); - hashtable->innerbatchPos = RELADDR(innerbatchPos); - hashtable->innerbatchSizes = RELADDR(innerbatchSizes); - } - else { - hashtable->outerbatchNames = (RelativeAddr)NULL; - hashtable->outerbatchPos = (RelativeAddr)NULL; - hashtable->innerbatchNames = (RelativeAddr)NULL; - hashtable->innerbatchPos = (RelativeAddr)NULL; - hashtable->innerbatchSizes = (RelativeAddr)NULL; - } - - hashtable->batch = (RelativeAddr)LONGALIGN(hashtable->top + - bucketsize * nbuckets); - hashtable->overflownext=hashtable->batch + nbatch * BLCKSZ; - /* ---------------- - * initialize each hash bucket - * ---------------- - */ - bucket = (HashBucket)ABSADDR(hashtable->top); - for (i=0; i<nbuckets; i++) { - bucket->top = RELADDR((char*)bucket + sizeof(*bucket)); - bucket->bottom = bucket->top; - bucket->firstotuple = bucket->lastotuple = -1; - bucket = (HashBucket)LONGALIGN(((char*)bucket + bucketsize)); - } - return(hashtable); + + hashtable->batch = (RelativeAddr) LONGALIGN(hashtable->top + + bucketsize * nbuckets); + hashtable->overflownext = hashtable->batch + nbatch * BLCKSZ; + /* ---------------- + * initialize each hash bucket + * ---------------- + */ + bucket = (HashBucket) ABSADDR(hashtable->top); + for (i = 0; i < nbuckets; i++) + { + bucket->top = RELADDR((char *) bucket + sizeof(*bucket)); + bucket->bottom = bucket->top; + bucket->firstotuple = bucket->lastotuple = -1; + bucket = (HashBucket) LONGALIGN(((char *) bucket + bucketsize)); + } + return (hashtable); } /* ---------------------------------------------------------------- - * ExecHashTableInsert + * ExecHashTableInsert * - * insert a tuple into the hash table depending on the hash value - * it may just go to a tmp file for other batches + * insert a tuple into the hash table depending on the hash value + * it may just go to a tmp file for other batches * ---------------------------------------------------------------- */ void ExecHashTableInsert(HashJoinTable hashtable, - ExprContext *econtext, - Var *hashkey, - File *batches) + ExprContext * econtext, + Var * hashkey, + File * batches) { - TupleTableSlot *slot; - HeapTuple heapTuple; - HashBucket bucket; - int bucketno; - int nbatch; - int batchno; - char *buffer; - RelativeAddr *batchPos; - int *batchSizes; - char *pos; - - nbatch = hashtable->nbatch; - batchPos = (RelativeAddr*)ABSADDR(hashtable->innerbatchPos); - batchSizes = (int*)ABSADDR(hashtable->innerbatchSizes); - - slot = econtext->ecxt_innertuple; - heapTuple = slot->val; - + TupleTableSlot *slot; + HeapTuple heapTuple; + HashBucket bucket; + int bucketno; + int nbatch; + int batchno; + char *buffer; + RelativeAddr *batchPos; + int *batchSizes; + char *pos; + + nbatch = hashtable->nbatch; + batchPos = (RelativeAddr *) ABSADDR(hashtable->innerbatchPos); + batchSizes = (int *) ABSADDR(hashtable->innerbatchSizes); + + slot = econtext->ecxt_innertuple; + heapTuple = slot->val; + #ifdef HJDEBUG - printf("Inserting "); + printf("Inserting "); #endif - - bucketno = ExecHashGetBucket(hashtable, econtext, hashkey); - - /* ---------------- - * decide whether to put the tuple in the hash table or a tmp file - * ---------------- - */ - if (bucketno < hashtable->nbuckets) { - /* --------------- - * put the tuple in hash table - * --------------- - */ - bucket = (HashBucket) - (ABSADDR(hashtable->top) + bucketno * hashtable->bucketsize); - if ((char*)LONGALIGN(ABSADDR(bucket->bottom)) - -(char*)bucket+heapTuple->t_len > hashtable->bucketsize) - ExecHashOverflowInsert(hashtable, bucket, heapTuple); - else { - memmove((char*)LONGALIGN(ABSADDR(bucket->bottom)), - heapTuple, - heapTuple->t_len); - bucket->bottom = - ((RelativeAddr)LONGALIGN(bucket->bottom) + heapTuple->t_len); + + bucketno = ExecHashGetBucket(hashtable, econtext, hashkey); + + /* ---------------- + * decide whether to put the tuple in the hash table or a tmp file + * ---------------- + */ + if (bucketno < hashtable->nbuckets) + { + /* --------------- + * put the tuple in hash table + * --------------- + */ + bucket = (HashBucket) + (ABSADDR(hashtable->top) + bucketno * hashtable->bucketsize); + if ((char *) LONGALIGN(ABSADDR(bucket->bottom)) + - (char *) bucket + heapTuple->t_len > hashtable->bucketsize) + ExecHashOverflowInsert(hashtable, bucket, heapTuple); + else + { + memmove((char *) LONGALIGN(ABSADDR(bucket->bottom)), + heapTuple, + heapTuple->t_len); + bucket->bottom = + ((RelativeAddr) LONGALIGN(bucket->bottom) + heapTuple->t_len); + } + } + else + { + /* ----------------- + * put the tuple into a tmp file for other batches + * ----------------- + */ + batchno = (float) (bucketno - hashtable->nbuckets) / + (float) (hashtable->totalbuckets - hashtable->nbuckets) + * nbatch; + buffer = ABSADDR(hashtable->batch) + batchno * BLCKSZ; + batchSizes[batchno]++; + pos = (char *) + ExecHashJoinSaveTuple(heapTuple, + buffer, + batches[batchno], + (char *) ABSADDR(batchPos[batchno])); + batchPos[batchno] = RELADDR(pos); } - } - else { - /* ----------------- - * put the tuple into a tmp file for other batches - * ----------------- - */ - batchno = (float)(bucketno - hashtable->nbuckets)/ - (float)(hashtable->totalbuckets - hashtable->nbuckets) - * nbatch; - buffer = ABSADDR(hashtable->batch) + batchno * BLCKSZ; - batchSizes[batchno]++; - pos= (char *) - ExecHashJoinSaveTuple(heapTuple, - buffer, - batches[batchno], - (char*)ABSADDR(batchPos[batchno])); - batchPos[batchno] = RELADDR(pos); - } } /* ---------------------------------------------------------------- - * ExecHashTableDestroy + * ExecHashTableDestroy * - * destroy a hash table + * destroy a hash table * ---------------------------------------------------------------- */ void ExecHashTableDestroy(HashJoinTable hashtable) { - pfree(hashtable); + pfree(hashtable); } /* ---------------------------------------------------------------- - * ExecHashGetBucket + * ExecHashGetBucket * - * Get the hash value for a tuple + * Get the hash value for a tuple * ---------------------------------------------------------------- */ int ExecHashGetBucket(HashJoinTable hashtable, - ExprContext *econtext, - Var *hashkey) + ExprContext * econtext, + Var * hashkey) { - int bucketno; - Datum keyval; - bool isNull; - - - /* ---------------- - * Get the join attribute value of the tuple - * ---------------- - * ...It's quick hack - use ExecEvalExpr instead of ExecEvalVar: - * hashkey may be T_ArrayRef, not just T_Var. - vadim 04/22/97 - */ - keyval = ExecEvalExpr((Node*)hashkey, econtext, &isNull, NULL); - - /* - * keyval could be null, so we better point it to something - * valid before trying to run hashFunc on it. --djm 8/17/96 - */ - if(isNull) { - execConstByVal = 0; - execConstLen = 0; - keyval = (Datum)""; - } - - /* ------------------ - * compute the hash function - * ------------------ - */ - if (execConstByVal) - bucketno = - hashFunc((char *) &keyval, execConstLen) % hashtable->totalbuckets; - else - bucketno = - hashFunc((char *) keyval, execConstLen) % hashtable->totalbuckets; + int bucketno; + Datum keyval; + bool isNull; + + + /* ---------------- + * Get the join attribute value of the tuple + * ---------------- + * ...It's quick hack - use ExecEvalExpr instead of ExecEvalVar: + * hashkey may be T_ArrayRef, not just T_Var. - vadim 04/22/97 + */ + keyval = ExecEvalExpr((Node *) hashkey, econtext, &isNull, NULL); + + /* + * keyval could be null, so we better point it to something valid + * before trying to run hashFunc on it. --djm 8/17/96 + */ + if (isNull) + { + execConstByVal = 0; + execConstLen = 0; + keyval = (Datum) ""; + } + + /* ------------------ + * compute the hash function + * ------------------ + */ + if (execConstByVal) + bucketno = + hashFunc((char *) &keyval, execConstLen) % hashtable->totalbuckets; + else + bucketno = + hashFunc((char *) keyval, execConstLen) % hashtable->totalbuckets; #ifdef HJDEBUG - if (bucketno >= hashtable->nbuckets) - printf("hash(%d) = %d SAVED\n", keyval, bucketno); - else - printf("hash(%d) = %d\n", keyval, bucketno); + if (bucketno >= hashtable->nbuckets) + printf("hash(%d) = %d SAVED\n", keyval, bucketno); + else + printf("hash(%d) = %d\n", keyval, bucketno); #endif - - return(bucketno); + + return (bucketno); } /* ---------------------------------------------------------------- - * ExecHashOverflowInsert + * ExecHashOverflowInsert * - * insert into the overflow area of a hash bucket + * insert into the overflow area of a hash bucket * ---------------------------------------------------------------- */ static void ExecHashOverflowInsert(HashJoinTable hashtable, - HashBucket bucket, - HeapTuple heapTuple) + HashBucket bucket, + HeapTuple heapTuple) { - OverflowTuple otuple; - RelativeAddr newend; - OverflowTuple firstotuple; - OverflowTuple lastotuple; - - firstotuple = (OverflowTuple)ABSADDR(bucket->firstotuple); - lastotuple = (OverflowTuple)ABSADDR(bucket->lastotuple); - /* ---------------- - * see if we run out of overflow space - * ---------------- - */ - newend = (RelativeAddr)LONGALIGN(hashtable->overflownext + sizeof(*otuple) - + heapTuple->t_len); - if (newend > hashtable->bottom) { -#if 0 - elog(DEBUG, "hash table out of memory. expanding."); - /* ------------------ - * XXX this is a temporary hack - * eventually, recursive hash partitioning will be - * implemented - * ------------------ + OverflowTuple otuple; + RelativeAddr newend; + OverflowTuple firstotuple; + OverflowTuple lastotuple; + + firstotuple = (OverflowTuple) ABSADDR(bucket->firstotuple); + lastotuple = (OverflowTuple) ABSADDR(bucket->lastotuple); + /* ---------------- + * see if we run out of overflow space + * ---------------- */ - hashtable->readbuf = hashtable->bottom = 2 * hashtable->bottom; - hashtable = - (HashJoinTable)repalloc(hashtable, hashtable->bottom+BLCKSZ); - if (hashtable == NULL) { - perror("repalloc"); - elog(WARN, "can't expand hashtable."); - } + newend = (RelativeAddr) LONGALIGN(hashtable->overflownext + sizeof(*otuple) + + heapTuple->t_len); + if (newend > hashtable->bottom) + { +#if 0 + elog(DEBUG, "hash table out of memory. expanding."); + /* ------------------ + * XXX this is a temporary hack + * eventually, recursive hash partitioning will be + * implemented + * ------------------ + */ + hashtable->readbuf = hashtable->bottom = 2 * hashtable->bottom; + hashtable = + (HashJoinTable) repalloc(hashtable, hashtable->bottom + BLCKSZ); + if (hashtable == NULL) + { + perror("repalloc"); + elog(WARN, "can't expand hashtable."); + } #else - /* ------------------ - * XXX the temporary hack above doesn't work because things - * above us don't know that we've moved the hash table! - * - Chris Dunlop, <chris@onthe.net.au> - * ------------------ - */ - elog(WARN, "hash table out of memory. Use -B parameter to increase buffers."); + /* ------------------ + * XXX the temporary hack above doesn't work because things + * above us don't know that we've moved the hash table! + * - Chris Dunlop, <chris@onthe.net.au> + * ------------------ + */ + elog(WARN, "hash table out of memory. Use -B parameter to increase buffers."); #endif - } - - /* ---------------- - * establish the overflow chain - * ---------------- - */ - otuple = (OverflowTuple)ABSADDR(hashtable->overflownext); - hashtable->overflownext = newend; - if (firstotuple == NULL) - bucket->firstotuple = bucket->lastotuple = RELADDR(otuple); - else { - lastotuple->next = RELADDR(otuple); - bucket->lastotuple = RELADDR(otuple); - } - - /* ---------------- - * copy the tuple into the overflow area - * ---------------- - */ - otuple->next = -1; - otuple->tuple = RELADDR(LONGALIGN(((char*)otuple + sizeof(*otuple)))); - memmove(ABSADDR(otuple->tuple), - heapTuple, - heapTuple->t_len); + } + + /* ---------------- + * establish the overflow chain + * ---------------- + */ + otuple = (OverflowTuple) ABSADDR(hashtable->overflownext); + hashtable->overflownext = newend; + if (firstotuple == NULL) + bucket->firstotuple = bucket->lastotuple = RELADDR(otuple); + else + { + lastotuple->next = RELADDR(otuple); + bucket->lastotuple = RELADDR(otuple); + } + + /* ---------------- + * copy the tuple into the overflow area + * ---------------- + */ + otuple->next = -1; + otuple->tuple = RELADDR(LONGALIGN(((char *) otuple + sizeof(*otuple)))); + memmove(ABSADDR(otuple->tuple), + heapTuple, + heapTuple->t_len); } /* ---------------------------------------------------------------- - * ExecScanHashBucket + * ExecScanHashBucket * - * scan a hash bucket of matches + * scan a hash bucket of matches * ---------------------------------------------------------------- */ HeapTuple -ExecScanHashBucket(HashJoinState *hjstate, - HashBucket bucket, - HeapTuple curtuple, - List *hjclauses, - ExprContext *econtext) +ExecScanHashBucket(HashJoinState * hjstate, + HashBucket bucket, + HeapTuple curtuple, + List * hjclauses, + ExprContext * econtext) { - HeapTuple heapTuple; - bool qualResult; - OverflowTuple otuple = NULL; - OverflowTuple curotuple; - TupleTableSlot *inntuple; - OverflowTuple firstotuple; - OverflowTuple lastotuple; - HashJoinTable hashtable; - - hashtable = hjstate->hj_HashTable; - firstotuple = (OverflowTuple)ABSADDR(bucket->firstotuple); - lastotuple = (OverflowTuple)ABSADDR(bucket->lastotuple); - - /* ---------------- - * search the hash bucket - * ---------------- - */ - if (curtuple == NULL || curtuple < (HeapTuple)ABSADDR(bucket->bottom)) { - if (curtuple == NULL) - heapTuple = (HeapTuple) - LONGALIGN(ABSADDR(bucket->top)); - else - heapTuple = (HeapTuple) - LONGALIGN(((char*)curtuple+curtuple->t_len)); - - while (heapTuple < (HeapTuple)ABSADDR(bucket->bottom)) { - - inntuple = ExecStoreTuple(heapTuple, /* tuple to store */ - hjstate->hj_HashTupleSlot, /* slot */ - InvalidBuffer,/* tuple has no buffer */ - false); /* do not pfree this tuple */ - - econtext->ecxt_innertuple = inntuple; - qualResult = ExecQual((List*)hjclauses, econtext); - - if (qualResult) - return heapTuple; - - heapTuple = (HeapTuple) - LONGALIGN(((char*)heapTuple+heapTuple->t_len)); + HeapTuple heapTuple; + bool qualResult; + OverflowTuple otuple = NULL; + OverflowTuple curotuple; + TupleTableSlot *inntuple; + OverflowTuple firstotuple; + OverflowTuple lastotuple; + HashJoinTable hashtable; + + hashtable = hjstate->hj_HashTable; + firstotuple = (OverflowTuple) ABSADDR(bucket->firstotuple); + lastotuple = (OverflowTuple) ABSADDR(bucket->lastotuple); + + /* ---------------- + * search the hash bucket + * ---------------- + */ + if (curtuple == NULL || curtuple < (HeapTuple) ABSADDR(bucket->bottom)) + { + if (curtuple == NULL) + heapTuple = (HeapTuple) + LONGALIGN(ABSADDR(bucket->top)); + else + heapTuple = (HeapTuple) + LONGALIGN(((char *) curtuple + curtuple->t_len)); + + while (heapTuple < (HeapTuple) ABSADDR(bucket->bottom)) + { + + inntuple = ExecStoreTuple(heapTuple, /* tuple to store */ + hjstate->hj_HashTupleSlot, /* slot */ + InvalidBuffer, /* tuple has no buffer */ + false); /* do not pfree this tuple */ + + econtext->ecxt_innertuple = inntuple; + qualResult = ExecQual((List *) hjclauses, econtext); + + if (qualResult) + return heapTuple; + + heapTuple = (HeapTuple) + LONGALIGN(((char *) heapTuple + heapTuple->t_len)); + } + + if (firstotuple == NULL) + return NULL; + otuple = firstotuple; } - - if (firstotuple == NULL) - return NULL; - otuple = firstotuple; - } - - /* ---------------- - * search the overflow area of the hash bucket - * ---------------- - */ - if (otuple == NULL) { - curotuple = hjstate->hj_CurOTuple; - otuple = (OverflowTuple)ABSADDR(curotuple->next); - } - - while (otuple != NULL) { - heapTuple = (HeapTuple)ABSADDR(otuple->tuple); - - inntuple = ExecStoreTuple(heapTuple, /* tuple to store */ - hjstate->hj_HashTupleSlot, /* slot */ - InvalidBuffer, /* SP?? this tuple has no buffer */ - false); /* do not pfree this tuple */ - - econtext->ecxt_innertuple = inntuple; - qualResult = ExecQual((List*)hjclauses, econtext); - - if (qualResult) { - hjstate->hj_CurOTuple = otuple; - return heapTuple; + + /* ---------------- + * search the overflow area of the hash bucket + * ---------------- + */ + if (otuple == NULL) + { + curotuple = hjstate->hj_CurOTuple; + otuple = (OverflowTuple) ABSADDR(curotuple->next); + } + + while (otuple != NULL) + { + heapTuple = (HeapTuple) ABSADDR(otuple->tuple); + + inntuple = ExecStoreTuple(heapTuple, /* tuple to store */ + hjstate->hj_HashTupleSlot, /* slot */ + InvalidBuffer, /* SP?? this tuple has + * no buffer */ + false); /* do not pfree this tuple */ + + econtext->ecxt_innertuple = inntuple; + qualResult = ExecQual((List *) hjclauses, econtext); + + if (qualResult) + { + hjstate->hj_CurOTuple = otuple; + return heapTuple; + } + + otuple = (OverflowTuple) ABSADDR(otuple->next); } - - otuple = (OverflowTuple)ABSADDR(otuple->next); - } - - /* ---------------- - * no match - * ---------------- - */ - return NULL; + + /* ---------------- + * no match + * ---------------- + */ + return NULL; } /* ---------------------------------------------------------------- - * hashFunc + * hashFunc * - * the hash function, copied from Margo + * the hash function, copied from Margo * ---------------------------------------------------------------- */ static int hashFunc(char *key, int len) { - register unsigned int h; - register int l; - register unsigned char *k; - - /* - * If this is a variable length type, then 'k' points - * to a "struct varlena" and len == -1. - * NOTE: - * VARSIZE returns the "real" data length plus the sizeof the - * "vl_len" attribute of varlena (the length information). - * 'k' points to the beginning of the varlena struct, so - * we have to use "VARDATA" to find the beginning of the "real" - * data. - */ - if (len == -1) { - l = VARSIZE(key) - VARHDRSZ; - k = (unsigned char*) VARDATA(key); - } else { - l = len; - k = (unsigned char *) key; - } - - h = 0; - - /* - * Convert string to integer - */ - while (l--) h = h * PRIME1 ^ (*k++); - h %= PRIME2; - - return (h); + register unsigned int h; + register int l; + register unsigned char *k; + + /* + * If this is a variable length type, then 'k' points to a "struct + * varlena" and len == -1. NOTE: VARSIZE returns the "real" data + * length plus the sizeof the "vl_len" attribute of varlena (the + * length information). 'k' points to the beginning of the varlena + * struct, so we have to use "VARDATA" to find the beginning of the + * "real" data. + */ + if (len == -1) + { + l = VARSIZE(key) - VARHDRSZ; + k = (unsigned char *) VARDATA(key); + } + else + { + l = len; + k = (unsigned char *) key; + } + + h = 0; + + /* + * Convert string to integer + */ + while (l--) + h = h * PRIME1 ^ (*k++); + h %= PRIME2; + + return (h); } /* ---------------------------------------------------------------- - * ExecHashPartition + * ExecHashPartition * - * determine the number of batches needed for a hashjoin + * determine the number of batches needed for a hashjoin * ---------------------------------------------------------------- */ static int -ExecHashPartition(Hash *node) +ExecHashPartition(Hash * node) { - Plan *outerNode; - int b; - int pages; - int ntuples; - int tupsize; - - /* - * get size information for plan node - */ - outerNode = outerPlan(node); - ntuples = outerNode->plan_size; - if (ntuples == 0) ntuples = 1000; - tupsize = outerNode->plan_width + sizeof(HeapTupleData); - pages = ceil((double)ntuples * tupsize * FUDGE_FAC / BLCKSZ); - - /* - * if amount of buffer space below hashjoin threshold, - * return negative - */ - if (ceil(sqrt((double)pages)) > HashTBSize) - return -1; - if (pages <= HashTBSize) - b = 0; /* fit in memory, no partitioning */ - else - b = ceil((double)(pages - HashTBSize)/(double)(HashTBSize - 1)); - - return b; + Plan *outerNode; + int b; + int pages; + int ntuples; + int tupsize; + + /* + * get size information for plan node + */ + outerNode = outerPlan(node); + ntuples = outerNode->plan_size; + if (ntuples == 0) + ntuples = 1000; + tupsize = outerNode->plan_width + sizeof(HeapTupleData); + pages = ceil((double) ntuples * tupsize * FUDGE_FAC / BLCKSZ); + + /* + * if amount of buffer space below hashjoin threshold, return negative + */ + if (ceil(sqrt((double) pages)) > HashTBSize) + return -1; + if (pages <= HashTBSize) + b = 0; /* fit in memory, no partitioning */ + else + b = ceil((double) (pages - HashTBSize) / (double) (HashTBSize - 1)); + + return b; } /* ---------------------------------------------------------------- - * ExecHashTableReset + * ExecHashTableReset * - * reset hash table header for new batch + * reset hash table header for new batch * ---------------------------------------------------------------- */ void ExecHashTableReset(HashJoinTable hashtable, int ntuples) { - int i; - HashBucket bucket; - - hashtable->nbuckets = hashtable->totalbuckets - = ceil((double)ntuples/NTUP_PER_BUCKET); - - hashtable->overflownext = hashtable->top + hashtable->bucketsize * - hashtable->nbuckets; - - bucket = (HashBucket)ABSADDR(hashtable->top); - for (i=0; i<hashtable->nbuckets; i++) { - bucket->top = RELADDR((char*)bucket + sizeof(*bucket)); - bucket->bottom = bucket->top; - bucket->firstotuple = bucket->lastotuple = -1; - bucket = (HashBucket)((char*)bucket + hashtable->bucketsize); - } - hashtable->pcount = hashtable->nprocess; + int i; + HashBucket bucket; + + hashtable->nbuckets = hashtable->totalbuckets + = ceil((double) ntuples / NTUP_PER_BUCKET); + + hashtable->overflownext = hashtable->top + hashtable->bucketsize * + hashtable->nbuckets; + + bucket = (HashBucket) ABSADDR(hashtable->top); + for (i = 0; i < hashtable->nbuckets; i++) + { + bucket->top = RELADDR((char *) bucket + sizeof(*bucket)); + bucket->bottom = bucket->top; + bucket->firstotuple = bucket->lastotuple = -1; + bucket = (HashBucket) ((char *) bucket + hashtable->bucketsize); + } + hashtable->pcount = hashtable->nprocess; } -static int hjtmpcnt = 0; +static int hjtmpcnt = 0; static void mk_hj_temp(char *tempname) { - sprintf(tempname, "HJ%d.%d", (int)getpid(), hjtmpcnt); - hjtmpcnt = (hjtmpcnt + 1) % 1000; + sprintf(tempname, "HJ%d.%d", (int) getpid(), hjtmpcnt); + hjtmpcnt = (hjtmpcnt + 1) % 1000; } - - - diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index c9f24efe193..3548e38cc86 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * nodeHashjoin.c-- - * Routines to handle hash join nodes + * Routines to handle hash join nodes * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.5 1997/08/19 21:31:09 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.6 1997/09/07 04:41:33 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -20,8 +20,8 @@ #include "postgres.h" -#include "storage/bufmgr.h" /* for BLCKSZ */ -#include "storage/fd.h" /* for SEEK_ */ +#include "storage/bufmgr.h" /* for BLCKSZ */ +#include "storage/fd.h" /* for SEEK_ */ #include "executor/executor.h" #include "executor/execdebug.h" #include "executor/nodeHash.h" @@ -33,775 +33,818 @@ #include "utils/palloc.h" static TupleTableSlot * -ExecHashJoinOuterGetTuple(Plan *node, Plan* parent, HashJoinState *hjstate); + ExecHashJoinOuterGetTuple(Plan * node, Plan * parent, HashJoinState * hjstate); static TupleTableSlot * -ExecHashJoinGetSavedTuple(HashJoinState *hjstate, char *buffer, - File file, TupleTableSlot *tupleSlot, int *block, char **position); +ExecHashJoinGetSavedTuple(HashJoinState * hjstate, char *buffer, + File file, TupleTableSlot * tupleSlot, int *block, char **position); -static int ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable, - int nbatch); +static int +ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable, + int nbatch); -static int ExecHashJoinNewBatch(HashJoinState *hjstate); +static int ExecHashJoinNewBatch(HashJoinState * hjstate); /* ---------------------------------------------------------------- - * ExecHashJoin + * ExecHashJoin * - * This function implements the Hybrid Hashjoin algorithm. - * recursive partitioning remains to be added. - * Note: the relation we build hash table on is the inner - * the other one is outer. + * This function implements the Hybrid Hashjoin algorithm. + * recursive partitioning remains to be added. + * Note: the relation we build hash table on is the inner + * the other one is outer. * ---------------------------------------------------------------- */ -TupleTableSlot * /* return: a tuple or NULL */ -ExecHashJoin(HashJoin *node) +TupleTableSlot * /* return: a tuple or NULL */ +ExecHashJoin(HashJoin * node) { - HashJoinState *hjstate; - EState *estate; - Plan *outerNode; - Hash *hashNode; - List *hjclauses; - Expr *clause; - List *qual; - ScanDirection dir; - TupleTableSlot *inntuple; - Var *outerVar; - ExprContext *econtext; - - HashJoinTable hashtable; - int bucketno; - HashBucket bucket; - HeapTuple curtuple; - - bool qualResult; - - TupleTableSlot *outerTupleSlot; - TupleTableSlot *innerTupleSlot; - int nbatch; - int curbatch; - File *outerbatches; - RelativeAddr *outerbatchNames; - RelativeAddr *outerbatchPos; - Var *innerhashkey; - int batch; - int batchno; - char *buffer; - int i; - bool hashPhaseDone; - char *pos; - - /* ---------------- - * get information from HashJoin node - * ---------------- - */ - hjstate = node->hashjoinstate; - hjclauses = node->hashclauses; - clause = lfirst(hjclauses); - estate = node->join.state; - qual = node->join.qual; - hashNode = (Hash *)innerPlan(node); - outerNode = outerPlan(node); - hashPhaseDone = node->hashdone; - - dir = estate->es_direction; - - /* ----------------- - * get information from HashJoin state - * ----------------- - */ - hashtable = hjstate->hj_HashTable; - bucket = hjstate->hj_CurBucket; - curtuple = hjstate->hj_CurTuple; - - /* -------------------- - * initialize expression context - * -------------------- - */ - econtext = hjstate->jstate.cs_ExprContext; - - if (hjstate->jstate.cs_TupFromTlist) { - TupleTableSlot *result; - bool isDone; - - result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone); - if (!isDone) - return result; - } - /* ---------------- - * if this is the first call, build the hash table for inner relation - * ---------------- - */ - if (!hashPhaseDone) { /* if the hash phase not completed */ - hashtable = node->hashjointable; - if (hashtable == NULL) { /* if the hash table has not been created */ - /* ---------------- - * create the hash table - * ---------------- - */ - hashtable = ExecHashTableCreate(hashNode); - hjstate->hj_HashTable = hashtable; - innerhashkey = hashNode->hashkey; - hjstate->hj_InnerHashKey = innerhashkey; - - /* ---------------- - * execute the Hash node, to build the hash table - * ---------------- - */ - hashNode->hashtable = hashtable; - innerTupleSlot = ExecProcNode((Plan *)hashNode, (Plan*) node); - } - bucket = NULL; - curtuple = NULL; - curbatch = 0; - node->hashdone = true; - } - nbatch = hashtable->nbatch; - outerbatches = hjstate->hj_OuterBatches; - if (nbatch > 0 && outerbatches == NULL) { /* if needs hash partition */ + HashJoinState *hjstate; + EState *estate; + Plan *outerNode; + Hash *hashNode; + List *hjclauses; + Expr *clause; + List *qual; + ScanDirection dir; + TupleTableSlot *inntuple; + Var *outerVar; + ExprContext *econtext; + + HashJoinTable hashtable; + int bucketno; + HashBucket bucket; + HeapTuple curtuple; + + bool qualResult; + + TupleTableSlot *outerTupleSlot; + TupleTableSlot *innerTupleSlot; + int nbatch; + int curbatch; + File *outerbatches; + RelativeAddr *outerbatchNames; + RelativeAddr *outerbatchPos; + Var *innerhashkey; + int batch; + int batchno; + char *buffer; + int i; + bool hashPhaseDone; + char *pos; + + /* ---------------- + * get information from HashJoin node + * ---------------- + */ + hjstate = node->hashjoinstate; + hjclauses = node->hashclauses; + clause = lfirst(hjclauses); + estate = node->join.state; + qual = node->join.qual; + hashNode = (Hash *) innerPlan(node); + outerNode = outerPlan(node); + hashPhaseDone = node->hashdone; + + dir = estate->es_direction; + /* ----------------- - * allocate space for file descriptors of outer batch files - * then open the batch files in the current process + * get information from HashJoin state * ----------------- */ - innerhashkey = hashNode->hashkey; - hjstate->hj_InnerHashKey = innerhashkey; - outerbatchNames = (RelativeAddr*) - ABSADDR(hashtable->outerbatchNames); - outerbatches = (File*) - palloc(nbatch * sizeof(File)); - for (i=0; i<nbatch; i++) { - outerbatches[i] = FileNameOpenFile( - ABSADDR(outerbatchNames[i]), - O_CREAT | O_RDWR, 0600); - } - hjstate->hj_OuterBatches = outerbatches; + hashtable = hjstate->hj_HashTable; + bucket = hjstate->hj_CurBucket; + curtuple = hjstate->hj_CurTuple; - /* ------------------ - * get the inner batch file descriptors from the - * hash node - * ------------------ - */ - hjstate->hj_InnerBatches = - hashNode->hashstate->hashBatches; - } - outerbatchPos = (RelativeAddr*)ABSADDR(hashtable->outerbatchPos); - curbatch = hashtable->curbatch; - outerbatchNames = (RelativeAddr*)ABSADDR(hashtable->outerbatchNames); - - /* ---------------- - * Now get an outer tuple and probe into the hash table for matches - * ---------------- - */ - outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot; - outerVar = get_leftop(clause); - - bucketno = -1; /* if bucketno remains -1, means use old outer tuple */ - if (TupIsNull(outerTupleSlot)) { - /* - * if the current outer tuple is nil, get a new one + /* -------------------- + * initialize expression context + * -------------------- */ - outerTupleSlot = (TupleTableSlot*) - ExecHashJoinOuterGetTuple(outerNode, (Plan*)node, hjstate); - - while (curbatch <= nbatch && TupIsNull(outerTupleSlot)) { - /* - * if the current batch runs out, switch to new batch - */ - curbatch = ExecHashJoinNewBatch(hjstate); - if (curbatch > nbatch) { - /* - * when the last batch runs out, clean up - */ - ExecHashTableDestroy(hashtable); - hjstate->hj_HashTable = NULL; - return NULL; - } - else - outerTupleSlot = (TupleTableSlot*) - ExecHashJoinOuterGetTuple(outerNode, (Plan*)node, hjstate); + econtext = hjstate->jstate.cs_ExprContext; + + if (hjstate->jstate.cs_TupFromTlist) + { + TupleTableSlot *result; + bool isDone; + + result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone); + if (!isDone) + return result; } - /* - * now we get an outer tuple, find the corresponding bucket for - * this tuple from the hash table - */ - econtext->ecxt_outertuple = outerTupleSlot; - -#ifdef HJDEBUG - printf("Probing "); -#endif - bucketno = ExecHashGetBucket(hashtable, econtext, outerVar); - bucket=(HashBucket)(ABSADDR(hashtable->top) - + bucketno * hashtable->bucketsize); - } - - for (;;) { /* ---------------- - * Now we've got an outer tuple and the corresponding hash bucket, - * but this tuple may not belong to the current batch. + * if this is the first call, build the hash table for inner relation * ---------------- */ - if (curbatch == 0 && bucketno != -1) /* if this is the first pass */ - batch = ExecHashJoinGetBatch(bucketno, hashtable, nbatch); - else - batch = 0; - if (batch > 0) { - /* - * if the current outer tuple does not belong to - * the current batch, save to the tmp file for - * the corresponding batch. - */ - buffer = ABSADDR(hashtable->batch) + (batch - 1) * BLCKSZ; - batchno = batch - 1; - pos = ExecHashJoinSaveTuple(outerTupleSlot->val, - buffer, - outerbatches[batchno], - ABSADDR(outerbatchPos[batchno])); - - outerbatchPos[batchno] = RELADDR(pos); + if (!hashPhaseDone) + { /* if the hash phase not completed */ + hashtable = node->hashjointable; + if (hashtable == NULL) + { /* if the hash table has not been created */ + /* ---------------- + * create the hash table + * ---------------- + */ + hashtable = ExecHashTableCreate(hashNode); + hjstate->hj_HashTable = hashtable; + innerhashkey = hashNode->hashkey; + hjstate->hj_InnerHashKey = innerhashkey; + + /* ---------------- + * execute the Hash node, to build the hash table + * ---------------- + */ + hashNode->hashtable = hashtable; + innerTupleSlot = ExecProcNode((Plan *) hashNode, (Plan *) node); + } + bucket = NULL; + curtuple = NULL; + curbatch = 0; + node->hashdone = true; } - else if (bucket != NULL) { - do { - /* - * scan the hash bucket for matches + nbatch = hashtable->nbatch; + outerbatches = hjstate->hj_OuterBatches; + if (nbatch > 0 && outerbatches == NULL) + { /* if needs hash partition */ + /* ----------------- + * allocate space for file descriptors of outer batch files + * then open the batch files in the current process + * ----------------- */ - curtuple = ExecScanHashBucket(hjstate, - bucket, - curtuple, - hjclauses, - econtext); - - if (curtuple != NULL) { - /* - * we've got a match, but still need to test qpqual - */ - inntuple = ExecStoreTuple(curtuple, - hjstate->hj_HashTupleSlot, - InvalidBuffer, - false); /* don't pfree this tuple */ - - econtext->ecxt_innertuple = inntuple; - - /* ---------------- - * test to see if we pass the qualification - * ---------------- - */ - qualResult = ExecQual((List*)qual, econtext); - - /* ---------------- - * if we pass the qual, then save state for next call and - * have ExecProject form the projection, store it - * in the tuple table, and return the slot. - * ---------------- - */ - if (qualResult) { - ProjectionInfo *projInfo; - TupleTableSlot *result; - bool isDone; - - hjstate->hj_CurBucket = bucket; - hjstate->hj_CurTuple = curtuple; - hashtable->curbatch = curbatch; - hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot; - - projInfo = hjstate->jstate.cs_ProjInfo; - result = ExecProject(projInfo, &isDone); - hjstate->jstate.cs_TupFromTlist = !isDone; - return result; - } + innerhashkey = hashNode->hashkey; + hjstate->hj_InnerHashKey = innerhashkey; + outerbatchNames = (RelativeAddr *) + ABSADDR(hashtable->outerbatchNames); + outerbatches = (File *) + palloc(nbatch * sizeof(File)); + for (i = 0; i < nbatch; i++) + { + outerbatches[i] = FileNameOpenFile( + ABSADDR(outerbatchNames[i]), + O_CREAT | O_RDWR, 0600); } - } - while (curtuple != NULL); + hjstate->hj_OuterBatches = outerbatches; + + /* ------------------ + * get the inner batch file descriptors from the + * hash node + * ------------------ + */ + hjstate->hj_InnerBatches = + hashNode->hashstate->hashBatches; } - + outerbatchPos = (RelativeAddr *) ABSADDR(hashtable->outerbatchPos); + curbatch = hashtable->curbatch; + outerbatchNames = (RelativeAddr *) ABSADDR(hashtable->outerbatchNames); + /* ---------------- - * Now the current outer tuple has run out of matches, - * so we free it and get a new outer tuple. + * Now get an outer tuple and probe into the hash table for matches * ---------------- */ - outerTupleSlot = (TupleTableSlot*) - ExecHashJoinOuterGetTuple(outerNode, (Plan*) node, hjstate); - - while (curbatch <= nbatch && TupIsNull(outerTupleSlot)) { - /* - * if the current batch runs out, switch to new batch - */ - curbatch = ExecHashJoinNewBatch(hjstate); - if (curbatch > nbatch) { + outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot; + outerVar = get_leftop(clause); + + bucketno = -1; /* if bucketno remains -1, means use old + * outer tuple */ + if (TupIsNull(outerTupleSlot)) + { + /* - * when the last batch runs out, clean up + * if the current outer tuple is nil, get a new one */ - ExecHashTableDestroy(hashtable); - hjstate->hj_HashTable = NULL; - return NULL; - } - else - outerTupleSlot = (TupleTableSlot*) - ExecHashJoinOuterGetTuple(outerNode, (Plan*)node, hjstate); + outerTupleSlot = (TupleTableSlot *) + ExecHashJoinOuterGetTuple(outerNode, (Plan *) node, hjstate); + + while (curbatch <= nbatch && TupIsNull(outerTupleSlot)) + { + + /* + * if the current batch runs out, switch to new batch + */ + curbatch = ExecHashJoinNewBatch(hjstate); + if (curbatch > nbatch) + { + + /* + * when the last batch runs out, clean up + */ + ExecHashTableDestroy(hashtable); + hjstate->hj_HashTable = NULL; + return NULL; + } + else + outerTupleSlot = (TupleTableSlot *) + ExecHashJoinOuterGetTuple(outerNode, (Plan *) node, hjstate); + } + + /* + * now we get an outer tuple, find the corresponding bucket for + * this tuple from the hash table + */ + econtext->ecxt_outertuple = outerTupleSlot; + +#ifdef HJDEBUG + printf("Probing "); +#endif + bucketno = ExecHashGetBucket(hashtable, econtext, outerVar); + bucket = (HashBucket) (ABSADDR(hashtable->top) + + bucketno * hashtable->bucketsize); } - - /* ---------------- - * Now get the corresponding hash bucket for the new - * outer tuple. - * ---------------- - */ - econtext->ecxt_outertuple = outerTupleSlot; + + for (;;) + { + /* ---------------- + * Now we've got an outer tuple and the corresponding hash bucket, + * but this tuple may not belong to the current batch. + * ---------------- + */ + if (curbatch == 0 && bucketno != -1) /* if this is the first + * pass */ + batch = ExecHashJoinGetBatch(bucketno, hashtable, nbatch); + else + batch = 0; + if (batch > 0) + { + + /* + * if the current outer tuple does not belong to the current + * batch, save to the tmp file for the corresponding batch. + */ + buffer = ABSADDR(hashtable->batch) + (batch - 1) * BLCKSZ; + batchno = batch - 1; + pos = ExecHashJoinSaveTuple(outerTupleSlot->val, + buffer, + outerbatches[batchno], + ABSADDR(outerbatchPos[batchno])); + + outerbatchPos[batchno] = RELADDR(pos); + } + else if (bucket != NULL) + { + do + { + + /* + * scan the hash bucket for matches + */ + curtuple = ExecScanHashBucket(hjstate, + bucket, + curtuple, + hjclauses, + econtext); + + if (curtuple != NULL) + { + + /* + * we've got a match, but still need to test qpqual + */ + inntuple = ExecStoreTuple(curtuple, + hjstate->hj_HashTupleSlot, + InvalidBuffer, + false); /* don't pfree this + * tuple */ + + econtext->ecxt_innertuple = inntuple; + + /* ---------------- + * test to see if we pass the qualification + * ---------------- + */ + qualResult = ExecQual((List *) qual, econtext); + + /* ---------------- + * if we pass the qual, then save state for next call and + * have ExecProject form the projection, store it + * in the tuple table, and return the slot. + * ---------------- + */ + if (qualResult) + { + ProjectionInfo *projInfo; + TupleTableSlot *result; + bool isDone; + + hjstate->hj_CurBucket = bucket; + hjstate->hj_CurTuple = curtuple; + hashtable->curbatch = curbatch; + hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot; + + projInfo = hjstate->jstate.cs_ProjInfo; + result = ExecProject(projInfo, &isDone); + hjstate->jstate.cs_TupFromTlist = !isDone; + return result; + } + } + } + while (curtuple != NULL); + } + + /* ---------------- + * Now the current outer tuple has run out of matches, + * so we free it and get a new outer tuple. + * ---------------- + */ + outerTupleSlot = (TupleTableSlot *) + ExecHashJoinOuterGetTuple(outerNode, (Plan *) node, hjstate); + + while (curbatch <= nbatch && TupIsNull(outerTupleSlot)) + { + + /* + * if the current batch runs out, switch to new batch + */ + curbatch = ExecHashJoinNewBatch(hjstate); + if (curbatch > nbatch) + { + + /* + * when the last batch runs out, clean up + */ + ExecHashTableDestroy(hashtable); + hjstate->hj_HashTable = NULL; + return NULL; + } + else + outerTupleSlot = (TupleTableSlot *) + ExecHashJoinOuterGetTuple(outerNode, (Plan *) node, hjstate); + } + + /* ---------------- + * Now get the corresponding hash bucket for the new + * outer tuple. + * ---------------- + */ + econtext->ecxt_outertuple = outerTupleSlot; #ifdef HJDEBUG - printf("Probing "); + printf("Probing "); #endif - bucketno = ExecHashGetBucket(hashtable, econtext, outerVar); - bucket=(HashBucket)(ABSADDR(hashtable->top) - + bucketno * hashtable->bucketsize); - curtuple = NULL; - } + bucketno = ExecHashGetBucket(hashtable, econtext, outerVar); + bucket = (HashBucket) (ABSADDR(hashtable->top) + + bucketno * hashtable->bucketsize); + curtuple = NULL; + } } /* ---------------------------------------------------------------- - * ExecInitHashJoin + * ExecInitHashJoin * - * Init routine for HashJoin node. + * Init routine for HashJoin node. * ---------------------------------------------------------------- */ -bool /* return: initialization status */ -ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent) +bool /* return: initialization status */ +ExecInitHashJoin(HashJoin * node, EState * estate, Plan * parent) { - HashJoinState *hjstate; - Plan *outerNode; - Hash *hashNode; - - /* ---------------- - * assign the node's execution state - * ---------------- - */ - node->join.state = estate; - - /* ---------------- - * create state structure - * ---------------- - */ - hjstate = makeNode(HashJoinState); - - node->hashjoinstate = hjstate; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks and - * + create expression context for node - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &hjstate->jstate, parent); - ExecAssignExprContext(estate, &hjstate->jstate); - + HashJoinState *hjstate; + Plan *outerNode; + Hash *hashNode; + + /* ---------------- + * assign the node's execution state + * ---------------- + */ + node->join.state = estate; + + /* ---------------- + * create state structure + * ---------------- + */ + hjstate = makeNode(HashJoinState); + + node->hashjoinstate = hjstate; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks and + * + create expression context for node + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &hjstate->jstate, parent); + ExecAssignExprContext(estate, &hjstate->jstate); + #define HASHJOIN_NSLOTS 2 - /* ---------------- - * tuple table initialization - * ---------------- - */ - ExecInitResultTupleSlot(estate, &hjstate->jstate); - ExecInitOuterTupleSlot(estate, hjstate); - - /* ---------------- - * initializes child nodes - * ---------------- - */ - outerNode = outerPlan((Plan *)node); - hashNode = (Hash*)innerPlan((Plan *)node); - - ExecInitNode(outerNode, estate, (Plan *) node); - ExecInitNode((Plan*)hashNode, estate, (Plan *) node); - - /* ---------------- - * now for some voodoo. our temporary tuple slot - * is actually the result tuple slot of the Hash node - * (which is our inner plan). we do this because Hash - * nodes don't return tuples via ExecProcNode() -- instead - * the hash join node uses ExecScanHashBucket() to get - * at the contents of the hash table. -cim 6/9/91 - * ---------------- - */ - { - HashState *hashstate = hashNode->hashstate; - TupleTableSlot *slot = - hashstate->cstate.cs_ResultTupleSlot; - hjstate->hj_HashTupleSlot = slot; - } - hjstate->hj_OuterTupleSlot->ttc_tupleDescriptor = - ExecGetTupType(outerNode); - + /* ---------------- + * tuple table initialization + * ---------------- + */ + ExecInitResultTupleSlot(estate, &hjstate->jstate); + ExecInitOuterTupleSlot(estate, hjstate); + + /* ---------------- + * initializes child nodes + * ---------------- + */ + outerNode = outerPlan((Plan *) node); + hashNode = (Hash *) innerPlan((Plan *) node); + + ExecInitNode(outerNode, estate, (Plan *) node); + ExecInitNode((Plan *) hashNode, estate, (Plan *) node); + + /* ---------------- + * now for some voodoo. our temporary tuple slot + * is actually the result tuple slot of the Hash node + * (which is our inner plan). we do this because Hash + * nodes don't return tuples via ExecProcNode() -- instead + * the hash join node uses ExecScanHashBucket() to get + * at the contents of the hash table. -cim 6/9/91 + * ---------------- + */ + { + HashState *hashstate = hashNode->hashstate; + TupleTableSlot *slot = + hashstate->cstate.cs_ResultTupleSlot; + + hjstate->hj_HashTupleSlot = slot; + } + hjstate->hj_OuterTupleSlot->ttc_tupleDescriptor = + ExecGetTupType(outerNode); + /* - hjstate->hj_OuterTupleSlot->ttc_execTupDescriptor = - ExecGetExecTupDesc(outerNode); + hjstate->hj_OuterTupleSlot->ttc_execTupDescriptor = + ExecGetExecTupDesc(outerNode); */ - - /* ---------------- - * initialize tuple type and projection info - * ---------------- - */ - ExecAssignResultTypeFromTL((Plan*) node, &hjstate->jstate); - ExecAssignProjectionInfo((Plan*) node, &hjstate->jstate); - - /* ---------------- - * XXX comment me - * ---------------- - */ - - node->hashdone = false; - - hjstate->hj_HashTable = (HashJoinTable)NULL; - hjstate->hj_HashTableShmId = (IpcMemoryId)0; - hjstate->hj_CurBucket = (HashBucket )NULL; - hjstate->hj_CurTuple = (HeapTuple )NULL; - hjstate->hj_CurOTuple = (OverflowTuple )NULL; - hjstate->hj_InnerHashKey = (Var*)NULL; - hjstate->hj_OuterBatches = (File*)NULL; - hjstate->hj_InnerBatches = (File*)NULL; - hjstate->hj_OuterReadPos = (char*)NULL; - hjstate->hj_OuterReadBlk = (int)0; - - hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot*) NULL; - hjstate->jstate.cs_TupFromTlist = (bool) false; - - return TRUE; + + /* ---------------- + * initialize tuple type and projection info + * ---------------- + */ + ExecAssignResultTypeFromTL((Plan *) node, &hjstate->jstate); + ExecAssignProjectionInfo((Plan *) node, &hjstate->jstate); + + /* ---------------- + * XXX comment me + * ---------------- + */ + + node->hashdone = false; + + hjstate->hj_HashTable = (HashJoinTable) NULL; + hjstate->hj_HashTableShmId = (IpcMemoryId) 0; + hjstate->hj_CurBucket = (HashBucket) NULL; + hjstate->hj_CurTuple = (HeapTuple) NULL; + hjstate->hj_CurOTuple = (OverflowTuple) NULL; + hjstate->hj_InnerHashKey = (Var *) NULL; + hjstate->hj_OuterBatches = (File *) NULL; + hjstate->hj_InnerBatches = (File *) NULL; + hjstate->hj_OuterReadPos = (char *) NULL; + hjstate->hj_OuterReadBlk = (int) 0; + + hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL; + hjstate->jstate.cs_TupFromTlist = (bool) false; + + return TRUE; } int -ExecCountSlotsHashJoin(HashJoin *node) +ExecCountSlotsHashJoin(HashJoin * node) { - return ExecCountSlotsNode(outerPlan(node)) + + return ExecCountSlotsNode(outerPlan(node)) + ExecCountSlotsNode(innerPlan(node)) + - HASHJOIN_NSLOTS; + HASHJOIN_NSLOTS; } /* ---------------------------------------------------------------- - * ExecEndHashJoin + * ExecEndHashJoin * - * clean up routine for HashJoin node + * clean up routine for HashJoin node * ---------------------------------------------------------------- */ void -ExecEndHashJoin(HashJoin *node) +ExecEndHashJoin(HashJoin * node) { - HashJoinState *hjstate; - - /* ---------------- - * get info from the HashJoin state - * ---------------- - */ - hjstate = node->hashjoinstate; - - /* ---------------- - * free hash table in case we end plan before all tuples are retrieved - * --------------- - */ - if (hjstate->hj_HashTable) { - ExecHashTableDestroy(hjstate->hj_HashTable); - hjstate->hj_HashTable = NULL; - } - - /* ---------------- - * Free the projection info and the scan attribute info - * - * Note: we don't ExecFreeResultType(hjstate) - * because the rule manager depends on the tupType - * returned by ExecMain(). So for now, this - * is freed at end-transaction time. -cim 6/2/91 - * ---------------- - */ - ExecFreeProjectionInfo(&hjstate->jstate); - - /* ---------------- - * clean up subtrees - * ---------------- - */ - ExecEndNode(outerPlan((Plan *) node), (Plan*)node); - ExecEndNode(innerPlan((Plan *) node), (Plan*)node); - - /* ---------------- - * clean out the tuple table - * ---------------- - */ - ExecClearTuple(hjstate->jstate.cs_ResultTupleSlot); - ExecClearTuple(hjstate->hj_OuterTupleSlot); - ExecClearTuple(hjstate->hj_HashTupleSlot); - + HashJoinState *hjstate; + + /* ---------------- + * get info from the HashJoin state + * ---------------- + */ + hjstate = node->hashjoinstate; + + /* ---------------- + * free hash table in case we end plan before all tuples are retrieved + * --------------- + */ + if (hjstate->hj_HashTable) + { + ExecHashTableDestroy(hjstate->hj_HashTable); + hjstate->hj_HashTable = NULL; + } + + /* ---------------- + * Free the projection info and the scan attribute info + * + * Note: we don't ExecFreeResultType(hjstate) + * because the rule manager depends on the tupType + * returned by ExecMain(). So for now, this + * is freed at end-transaction time. -cim 6/2/91 + * ---------------- + */ + ExecFreeProjectionInfo(&hjstate->jstate); + + /* ---------------- + * clean up subtrees + * ---------------- + */ + ExecEndNode(outerPlan((Plan *) node), (Plan *) node); + ExecEndNode(innerPlan((Plan *) node), (Plan *) node); + + /* ---------------- + * clean out the tuple table + * ---------------- + */ + ExecClearTuple(hjstate->jstate.cs_ResultTupleSlot); + ExecClearTuple(hjstate->hj_OuterTupleSlot); + ExecClearTuple(hjstate->hj_HashTupleSlot); + } /* ---------------------------------------------------------------- - * ExecHashJoinOuterGetTuple + * ExecHashJoinOuterGetTuple * - * get the next outer tuple for hashjoin: either by - * executing a plan node as in the first pass, or from - * the tmp files for the hashjoin batches. + * get the next outer tuple for hashjoin: either by + * executing a plan node as in the first pass, or from + * the tmp files for the hashjoin batches. * ---------------------------------------------------------------- */ static TupleTableSlot * -ExecHashJoinOuterGetTuple(Plan *node, Plan* parent, HashJoinState *hjstate) +ExecHashJoinOuterGetTuple(Plan * node, Plan * parent, HashJoinState * hjstate) { - TupleTableSlot *slot; - HashJoinTable hashtable; - int curbatch; - File *outerbatches; - char *outerreadPos; - int batchno; - char *outerreadBuf; - int outerreadBlk; - - hashtable = hjstate->hj_HashTable; - curbatch = hashtable->curbatch; - - if (curbatch == 0) { /* if it is the first pass */ - slot = ExecProcNode(node, parent); + TupleTableSlot *slot; + HashJoinTable hashtable; + int curbatch; + File *outerbatches; + char *outerreadPos; + int batchno; + char *outerreadBuf; + int outerreadBlk; + + hashtable = hjstate->hj_HashTable; + curbatch = hashtable->curbatch; + + if (curbatch == 0) + { /* if it is the first pass */ + slot = ExecProcNode(node, parent); + return slot; + } + + /* + * otherwise, read from the tmp files + */ + outerbatches = hjstate->hj_OuterBatches; + outerreadPos = hjstate->hj_OuterReadPos; + outerreadBlk = hjstate->hj_OuterReadBlk; + outerreadBuf = ABSADDR(hashtable->readbuf); + batchno = curbatch - 1; + + slot = ExecHashJoinGetSavedTuple(hjstate, + outerreadBuf, + outerbatches[batchno], + hjstate->hj_OuterTupleSlot, + &outerreadBlk, + &outerreadPos); + + hjstate->hj_OuterReadPos = outerreadPos; + hjstate->hj_OuterReadBlk = outerreadBlk; + return slot; - } - - /* - * otherwise, read from the tmp files - */ - outerbatches = hjstate->hj_OuterBatches; - outerreadPos = hjstate->hj_OuterReadPos; - outerreadBlk = hjstate->hj_OuterReadBlk; - outerreadBuf = ABSADDR(hashtable->readbuf); - batchno = curbatch - 1; - - slot = ExecHashJoinGetSavedTuple(hjstate, - outerreadBuf, - outerbatches[batchno], - hjstate->hj_OuterTupleSlot, - &outerreadBlk, - &outerreadPos); - - hjstate->hj_OuterReadPos = outerreadPos; - hjstate->hj_OuterReadBlk = outerreadBlk; - - return slot; } /* ---------------------------------------------------------------- - * ExecHashJoinGetSavedTuple + * ExecHashJoinGetSavedTuple * - * read the next tuple from a tmp file using a certain buffer + * read the next tuple from a tmp file using a certain buffer * ---------------------------------------------------------------- */ static TupleTableSlot * -ExecHashJoinGetSavedTuple(HashJoinState *hjstate, - char *buffer, - File file, - TupleTableSlot *tupleSlot, - int *block, /* return parameter */ - char **position) /* return parameter */ +ExecHashJoinGetSavedTuple(HashJoinState * hjstate, + char *buffer, + File file, + TupleTableSlot * tupleSlot, + int *block, /* return parameter */ + char **position) /* return parameter */ { - char *bufstart; - char *bufend; - int cc; - HeapTuple heapTuple; - HashJoinTable hashtable; - - hashtable = hjstate->hj_HashTable; - bufend = buffer + *(long*)buffer; - bufstart = (char*)(buffer + sizeof(long)); - if ((*position == NULL) || (*position >= bufend)) { - if (*position == NULL) - (*block) = 0; - else - (*block)++; - FileSeek(file, *block * BLCKSZ, SEEK_SET); - cc = FileRead(file, buffer, BLCKSZ); - NDirectFileRead++; - if (cc < 0) - perror("FileRead"); - if (cc == 0) /* end of file */ - return NULL; - else - (*position) = bufstart; - } - heapTuple = (HeapTuple) (*position); - (*position) = (char*)LONGALIGN(*position + heapTuple->t_len); - - return ExecStoreTuple(heapTuple,tupleSlot,InvalidBuffer,false); + char *bufstart; + char *bufend; + int cc; + HeapTuple heapTuple; + HashJoinTable hashtable; + + hashtable = hjstate->hj_HashTable; + bufend = buffer + *(long *) buffer; + bufstart = (char *) (buffer + sizeof(long)); + if ((*position == NULL) || (*position >= bufend)) + { + if (*position == NULL) + (*block) = 0; + else + (*block)++; + FileSeek(file, *block * BLCKSZ, SEEK_SET); + cc = FileRead(file, buffer, BLCKSZ); + NDirectFileRead++; + if (cc < 0) + perror("FileRead"); + if (cc == 0) /* end of file */ + return NULL; + else + (*position) = bufstart; + } + heapTuple = (HeapTuple) (*position); + (*position) = (char *) LONGALIGN(*position + heapTuple->t_len); + + return ExecStoreTuple(heapTuple, tupleSlot, InvalidBuffer, false); } /* ---------------------------------------------------------------- - * ExecHashJoinNewBatch + * ExecHashJoinNewBatch * - * switch to a new hashjoin batch + * switch to a new hashjoin batch * ---------------------------------------------------------------- */ static int -ExecHashJoinNewBatch(HashJoinState *hjstate) +ExecHashJoinNewBatch(HashJoinState * hjstate) { - File *innerBatches; - File *outerBatches; - int *innerBatchSizes; - Var *innerhashkey; - HashJoinTable hashtable; - int nbatch; - char *readPos; - int readBlk; - char *readBuf; - TupleTableSlot *slot; - ExprContext *econtext; - int i; - int cc; - int newbatch; - - hashtable = hjstate->hj_HashTable; - outerBatches = hjstate->hj_OuterBatches; - innerBatches = hjstate->hj_InnerBatches; - nbatch = hashtable->nbatch; - newbatch = hashtable->curbatch + 1; - - /* ------------------ - * this is the last process, so it will do the cleanup and - * batch-switching. - * ------------------ - */ - if (newbatch == 1) { - /* - * if it is end of the first pass, flush all the last pages for - * the batches. - */ - outerBatches = hjstate->hj_OuterBatches; - for (i=0; i<nbatch; i++) { - cc = FileSeek(outerBatches[i], 0L, SEEK_END); - if (cc < 0) - perror("FileSeek"); - cc = FileWrite(outerBatches[i], - ABSADDR(hashtable->batch) + i * BLCKSZ, BLCKSZ); - NDirectFileWrite++; - if (cc < 0) - perror("FileWrite"); - } + File *innerBatches; + File *outerBatches; + int *innerBatchSizes; + Var *innerhashkey; + HashJoinTable hashtable; + int nbatch; + char *readPos; + int readBlk; + char *readBuf; + TupleTableSlot *slot; + ExprContext *econtext; + int i; + int cc; + int newbatch; + + hashtable = hjstate->hj_HashTable; + outerBatches = hjstate->hj_OuterBatches; + innerBatches = hjstate->hj_InnerBatches; + nbatch = hashtable->nbatch; + newbatch = hashtable->curbatch + 1; + + /* ------------------ + * this is the last process, so it will do the cleanup and + * batch-switching. + * ------------------ + */ + if (newbatch == 1) + { + + /* + * if it is end of the first pass, flush all the last pages for + * the batches. + */ + outerBatches = hjstate->hj_OuterBatches; + for (i = 0; i < nbatch; i++) + { + cc = FileSeek(outerBatches[i], 0L, SEEK_END); + if (cc < 0) + perror("FileSeek"); + cc = FileWrite(outerBatches[i], + ABSADDR(hashtable->batch) + i * BLCKSZ, BLCKSZ); + NDirectFileWrite++; + if (cc < 0) + perror("FileWrite"); + } } - if (newbatch > 1) { + if (newbatch > 1) + { + + /* + * remove the previous outer batch + */ + FileUnlink(outerBatches[newbatch - 2]); + } + /* - * remove the previous outer batch + * rebuild the hash table for the new inner batch + */ + innerBatchSizes = (int *) ABSADDR(hashtable->innerbatchSizes); + /* -------------- + * skip over empty inner batches + * -------------- */ - FileUnlink(outerBatches[newbatch - 2]); - } - /* - * rebuild the hash table for the new inner batch - */ - innerBatchSizes = (int*)ABSADDR(hashtable->innerbatchSizes); - /* -------------- - * skip over empty inner batches - * -------------- - */ - while (newbatch <= nbatch && innerBatchSizes[newbatch - 1] == 0) { - FileUnlink(outerBatches[newbatch-1]); - FileUnlink(innerBatches[newbatch-1]); - newbatch++; + while (newbatch <= nbatch && innerBatchSizes[newbatch - 1] == 0) + { + FileUnlink(outerBatches[newbatch - 1]); + FileUnlink(innerBatches[newbatch - 1]); + newbatch++; } - if (newbatch > nbatch) { + if (newbatch > nbatch) + { + hashtable->pcount = hashtable->nprocess; + + return newbatch; + } + ExecHashTableReset(hashtable, innerBatchSizes[newbatch - 1]); + + + econtext = hjstate->jstate.cs_ExprContext; + innerhashkey = hjstate->hj_InnerHashKey; + readPos = NULL; + readBlk = 0; + readBuf = ABSADDR(hashtable->readbuf); + + while ((slot = ExecHashJoinGetSavedTuple(hjstate, + readBuf, + innerBatches[newbatch - 1], + hjstate->hj_HashTupleSlot, + &readBlk, + &readPos)) + && !TupIsNull(slot)) + { + econtext->ecxt_innertuple = slot; + ExecHashTableInsert(hashtable, econtext, innerhashkey, NULL); + /* possible bug - glass */ + } + + + /* ----------------- + * only the last process comes to this branch + * now all the processes have finished the build phase + * ---------------- + */ + + /* + * after we build the hash table, the inner batch is no longer needed + */ + FileUnlink(innerBatches[newbatch - 1]); + hjstate->hj_OuterReadPos = NULL; hashtable->pcount = hashtable->nprocess; + hashtable->curbatch = newbatch; return newbatch; - } - ExecHashTableReset(hashtable, innerBatchSizes[newbatch - 1]); - - - econtext = hjstate->jstate.cs_ExprContext; - innerhashkey = hjstate->hj_InnerHashKey; - readPos = NULL; - readBlk = 0; - readBuf = ABSADDR(hashtable->readbuf); - - while ((slot = ExecHashJoinGetSavedTuple(hjstate, - readBuf, - innerBatches[newbatch-1], - hjstate->hj_HashTupleSlot, - &readBlk, - &readPos)) - && ! TupIsNull(slot)) { - econtext->ecxt_innertuple = slot; - ExecHashTableInsert(hashtable, econtext, innerhashkey,NULL); - /* possible bug - glass */ - } - - - /* ----------------- - * only the last process comes to this branch - * now all the processes have finished the build phase - * ---------------- - */ - - /* - * after we build the hash table, the inner batch is no longer needed - */ - FileUnlink(innerBatches[newbatch - 1]); - hjstate->hj_OuterReadPos = NULL; - hashtable->pcount = hashtable->nprocess; - - hashtable->curbatch = newbatch; - return newbatch; } /* ---------------------------------------------------------------- - * ExecHashJoinGetBatch + * ExecHashJoinGetBatch * - * determine the batch number for a bucketno - * +----------------+-------+-------+ ... +-------+ - * 0 nbuckets totalbuckets - * batch 0 1 2 ... + * determine the batch number for a bucketno + * +----------------+-------+-------+ ... +-------+ + * 0 nbuckets totalbuckets + * batch 0 1 2 ... * ---------------------------------------------------------------- */ static int ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable, int nbatch) { - int b; - if (bucketno < hashtable->nbuckets || nbatch == 0) - return 0; - - b = (float)(bucketno - hashtable->nbuckets) / - (float)(hashtable->totalbuckets - hashtable->nbuckets) * - nbatch; - return b+1; + int b; + + if (bucketno < hashtable->nbuckets || nbatch == 0) + return 0; + + b = (float) (bucketno - hashtable->nbuckets) / + (float) (hashtable->totalbuckets - hashtable->nbuckets) * + nbatch; + return b + 1; } /* ---------------------------------------------------------------- - * ExecHashJoinSaveTuple + * ExecHashJoinSaveTuple * - * save a tuple to a tmp file using a buffer. - * the first few bytes in a page is an offset to the end - * of the page. + * save a tuple to a tmp file using a buffer. + * the first few bytes in a page is an offset to the end + * of the page. * ---------------------------------------------------------------- */ -char * +char * ExecHashJoinSaveTuple(HeapTuple heapTuple, - char *buffer, - File file, - char *position) + char *buffer, + File file, + char *position) { - long *pageend; - char *pagestart; - char *pagebound; - int cc; - - pageend = (long*)buffer; - pagestart = (char*)(buffer + sizeof(long)); - pagebound = buffer + BLCKSZ; - if (position == NULL) - position = pagestart; - - if (position + heapTuple->t_len >= pagebound) { - cc = FileSeek(file, 0L, SEEK_END); - if (cc < 0) - perror("FileSeek"); - cc = FileWrite(file, buffer, BLCKSZ); - NDirectFileWrite++; - if (cc < 0) - perror("FileWrite"); - position = pagestart; - *pageend = 0; - } - memmove(position, heapTuple, heapTuple->t_len); - position = (char*)LONGALIGN(position + heapTuple->t_len); - *pageend = position - buffer; - - return position; + long *pageend; + char *pagestart; + char *pagebound; + int cc; + + pageend = (long *) buffer; + pagestart = (char *) (buffer + sizeof(long)); + pagebound = buffer + BLCKSZ; + if (position == NULL) + position = pagestart; + + if (position + heapTuple->t_len >= pagebound) + { + cc = FileSeek(file, 0L, SEEK_END); + if (cc < 0) + perror("FileSeek"); + cc = FileWrite(file, buffer, BLCKSZ); + NDirectFileWrite++; + if (cc < 0) + perror("FileWrite"); + position = pagestart; + *pageend = 0; + } + memmove(position, heapTuple, heapTuple->t_len); + position = (char *) LONGALIGN(position + heapTuple->t_len); + *pageend = position - buffer; + + return position; } diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index bc0c4c3d288..c89a4fcb081 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -1,31 +1,31 @@ /*------------------------------------------------------------------------- * * nodeIndexscan.c-- - * Routines to support indexes and indexed scans of relations + * Routines to support indexes and indexed scans of relations * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.7 1997/03/12 20:58:26 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.8 1997/09/07 04:41:35 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * ExecInsertIndexTuples inserts tuples into indices on result relation + * ExecInsertIndexTuples inserts tuples into indices on result relation * - * ExecIndexScan scans a relation using indices - * ExecIndexNext using index to retrieve next tuple - * ExecInitIndexScan creates and initializes state info. - * ExecIndexReScan rescans the indexed relation. - * ExecEndIndexScan releases all storage. - * ExecIndexMarkPos marks scan position. - * ExecIndexRestrPos restores scan position. + * ExecIndexScan scans a relation using indices + * ExecIndexNext using index to retrieve next tuple + * ExecInitIndexScan creates and initializes state info. + * ExecIndexReScan rescans the indexed relation. + * ExecEndIndexScan releases all storage. + * ExecIndexMarkPos marks scan position. + * ExecIndexRestrPos restores scan position. * - * NOTES - * the code supporting ExecInsertIndexTuples should be - * collected and merged with the genam stuff. + * NOTES + * the code supporting ExecInsertIndexTuples should be + * collected and merged with the genam stuff. * */ #include "postgres.h" @@ -48,894 +48,939 @@ #include "nodes/nodeFuncs.h" /* ---------------- - * Misc stuff to move to executor.h soon -cim 6/5/90 + * Misc stuff to move to executor.h soon -cim 6/5/90 * ---------------- */ -#define NO_OP 0 -#define LEFT_OP 1 -#define RIGHT_OP 2 +#define NO_OP 0 +#define LEFT_OP 1 +#define RIGHT_OP 2 -static TupleTableSlot *IndexNext(IndexScan *node); +static TupleTableSlot *IndexNext(IndexScan * node); /* ---------------------------------------------------------------- - * IndexNext + * IndexNext * - * Retrieve a tuple from the IndexScan node's currentRelation - * using the indices in the IndexScanState information. + * Retrieve a tuple from the IndexScan node's currentRelation + * using the indices in the IndexScanState information. * - * note: the old code mentions 'Primary indices'. to my knowledge - * we only support a single secondary index. -cim 9/11/89 + * note: the old code mentions 'Primary indices'. to my knowledge + * we only support a single secondary index. -cim 9/11/89 * * old comments: - * retrieve a tuple from relation using the indices given. - * The indices are used in the order they appear in 'indices'. - * The indices may be primary or secondary indices: - * * primary index -- scan the relation 'relID' using keys supplied. - * * secondary index -- scan the index relation to get the 'tid' for - * a tuple in the relation 'relID'. - * If the current index(pointed by 'indexPtr') fails to return a - * tuple, the next index in the indices is used. - * - * bug fix so that it should retrieve on a null scan key. + * retrieve a tuple from relation using the indices given. + * The indices are used in the order they appear in 'indices'. + * The indices may be primary or secondary indices: + * * primary index -- scan the relation 'relID' using keys supplied. + * * secondary index -- scan the index relation to get the 'tid' for + * a tuple in the relation 'relID'. + * If the current index(pointed by 'indexPtr') fails to return a + * tuple, the next index in the indices is used. + * + * bug fix so that it should retrieve on a null scan key. * ---------------------------------------------------------------- */ static TupleTableSlot * -IndexNext(IndexScan *node) +IndexNext(IndexScan * node) { - EState *estate; - CommonScanState *scanstate; - IndexScanState *indexstate; - ScanDirection direction; - int indexPtr; - IndexScanDescPtr scanDescs; - IndexScanDesc scandesc; - Relation heapRelation; - RetrieveIndexResult result; - ItemPointer iptr; - HeapTuple tuple; - TupleTableSlot *slot; - Buffer buffer = InvalidBuffer; - - /* ---------------- - * extract necessary information from index scan node - * ---------------- - */ - estate = node->scan.plan.state; - direction = estate->es_direction; - scanstate = node->scan.scanstate; - indexstate = node->indxstate; - indexPtr = indexstate->iss_IndexPtr; - scanDescs = indexstate->iss_ScanDescs; - scandesc = scanDescs[ indexPtr ]; - heapRelation = scanstate->css_currentRelation; - - slot = scanstate->css_ScanTupleSlot; - - /* ---------------- - * ok, now that we have what we need, fetch an index tuple. - * ---------------- - */ - - for(;;) { - result = index_getnext(scandesc, direction); + EState *estate; + CommonScanState *scanstate; + IndexScanState *indexstate; + ScanDirection direction; + int indexPtr; + IndexScanDescPtr scanDescs; + IndexScanDesc scandesc; + Relation heapRelation; + RetrieveIndexResult result; + ItemPointer iptr; + HeapTuple tuple; + TupleTableSlot *slot; + Buffer buffer = InvalidBuffer; + /* ---------------- - * if scanning this index succeeded then return the - * appropriate heap tuple.. else return NULL. + * extract necessary information from index scan node * ---------------- */ - if (result) { - iptr = &result->heap_iptr; - tuple = heap_fetch(heapRelation, - NowTimeQual, - iptr, - &buffer); - /* be tidy */ - pfree(result); - - if (tuple == NULL) { + estate = node->scan.plan.state; + direction = estate->es_direction; + scanstate = node->scan.scanstate; + indexstate = node->indxstate; + indexPtr = indexstate->iss_IndexPtr; + scanDescs = indexstate->iss_ScanDescs; + scandesc = scanDescs[indexPtr]; + heapRelation = scanstate->css_currentRelation; + + slot = scanstate->css_ScanTupleSlot; + + /* ---------------- + * ok, now that we have what we need, fetch an index tuple. + * ---------------- + */ + + for (;;) + { + result = index_getnext(scandesc, direction); + /* ---------------- + * if scanning this index succeeded then return the + * appropriate heap tuple.. else return NULL. + * ---------------- + */ + if (result) + { + iptr = &result->heap_iptr; + tuple = heap_fetch(heapRelation, + NowTimeQual, + iptr, + &buffer); + /* be tidy */ + pfree(result); + + if (tuple == NULL) + { + /* ---------------- + * we found a deleted tuple, so keep on scanning.. + * ---------------- + */ + if (BufferIsValid(buffer)) + ReleaseBuffer(buffer); + continue; + } + + /* ---------------- + * store the scanned tuple in the scan tuple slot of + * the scan state. Eventually we will only do this and not + * return a tuple. Note: we pass 'false' because tuples + * returned by amgetnext are pointers onto disk pages and + * were not created with palloc() and so should not be pfree()'d. + * ---------------- + */ + ExecStoreTuple(tuple, /* tuple to store */ + slot,/* slot to store in */ + buffer, /* buffer associated with tuple */ + false); /* don't pfree */ + + return slot; + } + /* ---------------- - * we found a deleted tuple, so keep on scanning.. + * if we get here it means the index scan failed so we + * are at the end of the scan.. * ---------------- */ - if (BufferIsValid(buffer)) - ReleaseBuffer(buffer); - continue; - } - - /* ---------------- - * store the scanned tuple in the scan tuple slot of - * the scan state. Eventually we will only do this and not - * return a tuple. Note: we pass 'false' because tuples - * returned by amgetnext are pointers onto disk pages and - * were not created with palloc() and so should not be pfree()'d. - * ---------------- - */ - ExecStoreTuple(tuple, /* tuple to store */ - slot, /* slot to store in */ - buffer, /* buffer associated with tuple */ - false); /* don't pfree */ - - return slot; + return ExecClearTuple(slot); } - - /* ---------------- - * if we get here it means the index scan failed so we - * are at the end of the scan.. - * ---------------- - */ - return ExecClearTuple(slot); - } } /* ---------------------------------------------------------------- - * ExecIndexScan(node) + * ExecIndexScan(node) * * old comments: - * Scans the relation using primary or secondary indices and returns - * the next qualifying tuple in the direction specified. - * It calls ExecScan() and passes it the access methods which returns - * the next tuple using the indices. - * - * Conditions: - * -- the "cursor" maintained by the AMI is positioned at the tuple - * returned previously. - * - * Initial States: - * -- the relation indicated is opened for scanning so that the - * "cursor" is positioned before the first qualifying tuple. - * -- all index realtions are opened for scanning. - * -- indexPtr points to the first index. - * -- state variable ruleFlag = nil. + * Scans the relation using primary or secondary indices and returns + * the next qualifying tuple in the direction specified. + * It calls ExecScan() and passes it the access methods which returns + * the next tuple using the indices. + * + * Conditions: + * -- the "cursor" maintained by the AMI is positioned at the tuple + * returned previously. + * + * Initial States: + * -- the relation indicated is opened for scanning so that the + * "cursor" is positioned before the first qualifying tuple. + * -- all index realtions are opened for scanning. + * -- indexPtr points to the first index. + * -- state variable ruleFlag = nil. * ---------------------------------------------------------------- */ TupleTableSlot * -ExecIndexScan(IndexScan *node) +ExecIndexScan(IndexScan * node) { - TupleTableSlot *returnTuple; + TupleTableSlot *returnTuple; - /* ---------------- - * use IndexNext as access method - * ---------------- - */ - returnTuple = ExecScan(&node->scan, IndexNext); - return returnTuple; -} + /* ---------------- + * use IndexNext as access method + * ---------------- + */ + returnTuple = ExecScan(&node->scan, IndexNext); + return returnTuple; +} /* ---------------------------------------------------------------- - * ExecIndexReScan(node) + * ExecIndexReScan(node) * - * Recalculates the value of the scan keys whose value depends on - * information known at runtime and rescans the indexed relation. - * Updating the scan key was formerly done separately in - * ExecUpdateIndexScanKeys. Integrating it into ReScan - * makes rescans of indices and - * relations/general streams more uniform. + * Recalculates the value of the scan keys whose value depends on + * information known at runtime and rescans the indexed relation. + * Updating the scan key was formerly done separately in + * ExecUpdateIndexScanKeys. Integrating it into ReScan + * makes rescans of indices and + * relations/general streams more uniform. * * ---------------------------------------------------------------- */ void -ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan* parent) +ExecIndexReScan(IndexScan * node, ExprContext * exprCtxt, Plan * parent) { - EState *estate; - IndexScanState *indexstate; - ScanDirection direction; - IndexScanDescPtr scanDescs; - ScanKey *scanKeys; - IndexScanDesc sdesc; - ScanKey skey; - int numIndices; - int i; - - Pointer *runtimeKeyInfo; - int indexPtr; - int *numScanKeys; - List *indxqual; - List *qual; - int n_keys; - ScanKey scan_keys; - int *run_keys; - int j; - Expr *clause; - Node *scanexpr; - Datum scanvalue; - bool isNull; - bool isDone; - - indexstate = node->indxstate; - estate = node->scan.plan.state; - direction = estate->es_direction; - indexstate = node->indxstate; - numIndices = indexstate->iss_NumIndices; - scanDescs = indexstate->iss_ScanDescs; - scanKeys = indexstate->iss_ScanKeys; - - runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo; - - if (runtimeKeyInfo != NULL) { - /* - * get the index qualifications and - * recalculate the appropriate values - */ - indexPtr = indexstate->iss_IndexPtr; - indxqual = node->indxqual; - qual = nth(indexPtr, indxqual); - numScanKeys = indexstate->iss_NumScanKeys; - n_keys = numScanKeys[indexPtr]; - run_keys = (int *) runtimeKeyInfo[indexPtr]; - scan_keys = (ScanKey) scanKeys[indexPtr]; - - for (j=0; j < n_keys; j++) { - /* - * If we have a run-time key, then extract the run-time - * expression and evaluate it with respect to the current - * outer tuple. We then stick the result into the scan - * key. - */ - if (run_keys[j] != NO_OP) { - clause = nth(j, qual); - scanexpr = (run_keys[j] == RIGHT_OP) ? - (Node*) get_rightop(clause) : (Node*) get_leftop(clause) ; - /* pass in isDone but ignore it. We don't iterate in quals */ - scanvalue = (Datum) - ExecEvalExpr(scanexpr, exprCtxt, &isNull, &isDone); - scan_keys[j].sk_argument = scanvalue; - if (isNull) { - scan_keys[j].sk_flags |= SK_ISNULL; - } else { - scan_keys[j].sk_flags &= ~SK_ISNULL; + EState *estate; + IndexScanState *indexstate; + ScanDirection direction; + IndexScanDescPtr scanDescs; + ScanKey *scanKeys; + IndexScanDesc sdesc; + ScanKey skey; + int numIndices; + int i; + + Pointer *runtimeKeyInfo; + int indexPtr; + int *numScanKeys; + List *indxqual; + List *qual; + int n_keys; + ScanKey scan_keys; + int *run_keys; + int j; + Expr *clause; + Node *scanexpr; + Datum scanvalue; + bool isNull; + bool isDone; + + indexstate = node->indxstate; + estate = node->scan.plan.state; + direction = estate->es_direction; + indexstate = node->indxstate; + numIndices = indexstate->iss_NumIndices; + scanDescs = indexstate->iss_ScanDescs; + scanKeys = indexstate->iss_ScanKeys; + + runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo; + + if (runtimeKeyInfo != NULL) + { + + /* + * get the index qualifications and recalculate the appropriate + * values + */ + indexPtr = indexstate->iss_IndexPtr; + indxqual = node->indxqual; + qual = nth(indexPtr, indxqual); + numScanKeys = indexstate->iss_NumScanKeys; + n_keys = numScanKeys[indexPtr]; + run_keys = (int *) runtimeKeyInfo[indexPtr]; + scan_keys = (ScanKey) scanKeys[indexPtr]; + + for (j = 0; j < n_keys; j++) + { + + /* + * If we have a run-time key, then extract the run-time + * expression and evaluate it with respect to the current + * outer tuple. We then stick the result into the scan key. + */ + if (run_keys[j] != NO_OP) + { + clause = nth(j, qual); + scanexpr = (run_keys[j] == RIGHT_OP) ? + (Node *) get_rightop(clause) : (Node *) get_leftop(clause); + + /* + * pass in isDone but ignore it. We don't iterate in + * quals + */ + scanvalue = (Datum) + ExecEvalExpr(scanexpr, exprCtxt, &isNull, &isDone); + scan_keys[j].sk_argument = scanvalue; + if (isNull) + { + scan_keys[j].sk_flags |= SK_ISNULL; + } + else + { + scan_keys[j].sk_flags &= ~SK_ISNULL; + } + } } - } } - } - - /* - * rescans all indices - * - * note: AMrescan assumes only one scan key. This may have - * to change if we ever decide to support multiple keys. - */ - for (i = 0; i < numIndices; i++) { - sdesc = scanDescs[ i ]; - skey = scanKeys[ i ]; - index_rescan(sdesc, direction, skey); - } - - /* ---------------- - * perhaps return something meaningful - * ---------------- - */ - return; + + /* + * rescans all indices + * + * note: AMrescan assumes only one scan key. This may have to change if + * we ever decide to support multiple keys. + */ + for (i = 0; i < numIndices; i++) + { + sdesc = scanDescs[i]; + skey = scanKeys[i]; + index_rescan(sdesc, direction, skey); + } + + /* ---------------- + * perhaps return something meaningful + * ---------------- + */ + return; } /* ---------------------------------------------------------------- - * ExecEndIndexScan + * ExecEndIndexScan * * old comments - * Releases any storage allocated through C routines. - * Returns nothing. + * Releases any storage allocated through C routines. + * Returns nothing. * ---------------------------------------------------------------- */ void -ExecEndIndexScan(IndexScan *node) +ExecEndIndexScan(IndexScan * node) { - CommonScanState *scanstate; - IndexScanState *indexstate; - ScanKey *scanKeys; - int numIndices; - int i; - - scanstate = node->scan.scanstate; - indexstate = node->indxstate; - - /* ---------------- - * extract information from the node - * ---------------- - */ - numIndices = indexstate->iss_NumIndices; - scanKeys = indexstate->iss_ScanKeys; - - /* ---------------- - * Free the projection info and the scan attribute info - * - * Note: we don't ExecFreeResultType(scanstate) - * because the rule manager depends on the tupType - * returned by ExecMain(). So for now, this - * is freed at end-transaction time. -cim 6/2/91 - * ---------------- - */ - ExecFreeProjectionInfo(&scanstate->cstate); - - /* ---------------- - * close the heap and index relations - * ---------------- - */ - ExecCloseR((Plan *) node); - - /* ---------------- - * free the scan keys used in scanning the indices - * ---------------- - */ - for (i=0; i<numIndices; i++) { - if (scanKeys[i]!=NULL) - pfree(scanKeys[i]); - - } - - /* ---------------- - * clear out tuple table slots - * ---------------- - */ - ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot); - ExecClearTuple(scanstate->css_ScanTupleSlot); -/* ExecClearTuple(scanstate->css_RawTupleSlot); */ + CommonScanState *scanstate; + IndexScanState *indexstate; + ScanKey *scanKeys; + int numIndices; + int i; + + scanstate = node->scan.scanstate; + indexstate = node->indxstate; + + /* ---------------- + * extract information from the node + * ---------------- + */ + numIndices = indexstate->iss_NumIndices; + scanKeys = indexstate->iss_ScanKeys; + + /* ---------------- + * Free the projection info and the scan attribute info + * + * Note: we don't ExecFreeResultType(scanstate) + * because the rule manager depends on the tupType + * returned by ExecMain(). So for now, this + * is freed at end-transaction time. -cim 6/2/91 + * ---------------- + */ + ExecFreeProjectionInfo(&scanstate->cstate); + + /* ---------------- + * close the heap and index relations + * ---------------- + */ + ExecCloseR((Plan *) node); + + /* ---------------- + * free the scan keys used in scanning the indices + * ---------------- + */ + for (i = 0; i < numIndices; i++) + { + if (scanKeys[i] != NULL) + pfree(scanKeys[i]); + + } + + /* ---------------- + * clear out tuple table slots + * ---------------- + */ + ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot); + ExecClearTuple(scanstate->css_ScanTupleSlot); +/* ExecClearTuple(scanstate->css_RawTupleSlot); */ } /* ---------------------------------------------------------------- - * ExecIndexMarkPos + * ExecIndexMarkPos * * old comments - * Marks scan position by marking the current index. - * Returns nothing. + * Marks scan position by marking the current index. + * Returns nothing. * ---------------------------------------------------------------- */ void -ExecIndexMarkPos(IndexScan *node) +ExecIndexMarkPos(IndexScan * node) { - IndexScanState *indexstate; - IndexScanDescPtr indexScanDescs; - IndexScanDesc scanDesc; - int indexPtr; - - indexstate = node->indxstate; - indexPtr = indexstate->iss_IndexPtr; - indexScanDescs = indexstate->iss_ScanDescs; - scanDesc = indexScanDescs[ indexPtr ]; - - /* ---------------- - * XXX access methods don't return marked positions so - * ---------------- - */ - IndexScanMarkPosition( scanDesc ); - return; + IndexScanState *indexstate; + IndexScanDescPtr indexScanDescs; + IndexScanDesc scanDesc; + int indexPtr; + + indexstate = node->indxstate; + indexPtr = indexstate->iss_IndexPtr; + indexScanDescs = indexstate->iss_ScanDescs; + scanDesc = indexScanDescs[indexPtr]; + + /* ---------------- + * XXX access methods don't return marked positions so + * ---------------- + */ + IndexScanMarkPosition(scanDesc); + return; } /* ---------------------------------------------------------------- - * ExecIndexRestrPos + * ExecIndexRestrPos * * old comments - * Restores scan position by restoring the current index. - * Returns nothing. - * - * XXX Assumes previously marked scan position belongs to current index + * Restores scan position by restoring the current index. + * Returns nothing. + * + * XXX Assumes previously marked scan position belongs to current index * ---------------------------------------------------------------- */ void -ExecIndexRestrPos(IndexScan *node) +ExecIndexRestrPos(IndexScan * node) { - IndexScanState *indexstate; - IndexScanDescPtr indexScanDescs; - IndexScanDesc scanDesc; - int indexPtr; + IndexScanState *indexstate; + IndexScanDescPtr indexScanDescs; + IndexScanDesc scanDesc; + int indexPtr; - indexstate = node->indxstate; - indexPtr = indexstate->iss_IndexPtr; - indexScanDescs = indexstate->iss_ScanDescs; - scanDesc = indexScanDescs[ indexPtr ]; + indexstate = node->indxstate; + indexPtr = indexstate->iss_IndexPtr; + indexScanDescs = indexstate->iss_ScanDescs; + scanDesc = indexScanDescs[indexPtr]; - IndexScanRestorePosition( scanDesc ); + IndexScanRestorePosition(scanDesc); } /* ---------------------------------------------------------------- - * ExecInitIndexScan + * ExecInitIndexScan + * + * Initializes the index scan's state information, creates + * scan keys, and opens the base and index relations. * - * Initializes the index scan's state information, creates - * scan keys, and opens the base and index relations. + * Note: index scans have 2 sets of state information because + * we have to keep track of the base relation and the + * index relations. * - * Note: index scans have 2 sets of state information because - * we have to keep track of the base relation and the - * index relations. - * * old comments - * Creates the run-time state information for the node and - * sets the relation id to contain relevant decriptors. - * - * Parameters: - * node: IndexNode node produced by the planner. - * estate: the execution state initialized in InitPlan. + * Creates the run-time state information for the node and + * sets the relation id to contain relevant decriptors. + * + * Parameters: + * node: IndexNode node produced by the planner. + * estate: the execution state initialized in InitPlan. * ---------------------------------------------------------------- */ bool -ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) +ExecInitIndexScan(IndexScan * node, EState * estate, Plan * parent) { - IndexScanState *indexstate; - CommonScanState *scanstate; - List *indxqual; - List *indxid; - int i; - int numIndices; - int indexPtr; - ScanKey *scanKeys; - int *numScanKeys; - RelationPtr relationDescs; - IndexScanDescPtr scanDescs; - Pointer *runtimeKeyInfo; - bool have_runtime_keys; - List *rangeTable; - RangeTblEntry *rtentry; - Index relid; - Oid reloid; - TimeQual timeQual; - - Relation currentRelation; - HeapScanDesc currentScanDesc; - ScanDirection direction; - int baseid; - - /* ---------------- - * assign execution state to node - * ---------------- - */ - node->scan.plan.state = estate; - - /* -------------------------------- - * Part 1) initialize scan state - * - * create new CommonScanState for node - * -------------------------------- - */ - scanstate = makeNode(CommonScanState); + IndexScanState *indexstate; + CommonScanState *scanstate; + List *indxqual; + List *indxid; + int i; + int numIndices; + int indexPtr; + ScanKey *scanKeys; + int *numScanKeys; + RelationPtr relationDescs; + IndexScanDescPtr scanDescs; + Pointer *runtimeKeyInfo; + bool have_runtime_keys; + List *rangeTable; + RangeTblEntry *rtentry; + Index relid; + Oid reloid; + TimeQual timeQual; + + Relation currentRelation; + HeapScanDesc currentScanDesc; + ScanDirection direction; + int baseid; + + /* ---------------- + * assign execution state to node + * ---------------- + */ + node->scan.plan.state = estate; + + /* -------------------------------- + * Part 1) initialize scan state + * + * create new CommonScanState for node + * -------------------------------- + */ + scanstate = makeNode(CommonScanState); /* - scanstate->ss_ProcOuterFlag = false; - scanstate->ss_OldRelId = 0; + scanstate->ss_ProcOuterFlag = false; + scanstate->ss_OldRelId = 0; */ - node->scan.scanstate = scanstate; + node->scan.scanstate = scanstate; - /* ---------------- - * assign node's base_id .. we don't use AssignNodeBaseid() because - * the increment is done later on after we assign the index scan's - * scanstate. see below. - * ---------------- - */ - baseid = estate->es_BaseId; -/* scanstate->csstate.cstate.bnode.base_id = baseid; */ - scanstate->cstate.cs_base_id = baseid; + /* ---------------- + * assign node's base_id .. we don't use AssignNodeBaseid() because + * the increment is done later on after we assign the index scan's + * scanstate. see below. + * ---------------- + */ + baseid = estate->es_BaseId; +/* scanstate->csstate.cstate.bnode.base_id = baseid; */ + scanstate->cstate.cs_base_id = baseid; - /* ---------------- - * create expression context for node - * ---------------- - */ - ExecAssignExprContext(estate, &scanstate->cstate); + /* ---------------- + * create expression context for node + * ---------------- + */ + ExecAssignExprContext(estate, &scanstate->cstate); #define INDEXSCAN_NSLOTS 3 - /* ---------------- - * tuple table initialization - * ---------------- - */ - ExecInitResultTupleSlot(estate, &scanstate->cstate); - ExecInitScanTupleSlot(estate, scanstate); -/* ExecInitRawTupleSlot(estate, scanstate); */ - - /* ---------------- - * initialize projection info. result type comes from scan desc - * below.. - * ---------------- - */ - ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate); - - /* -------------------------------- - * Part 2) initialize index scan state - * - * create new IndexScanState for node - * -------------------------------- - */ - indexstate = makeNode(IndexScanState); - indexstate->iss_NumIndices = 0; - indexstate->iss_IndexPtr = 0; - indexstate->iss_ScanKeys = NULL; - indexstate->iss_NumScanKeys = NULL; - indexstate->iss_RuntimeKeyInfo = NULL; - indexstate->iss_RelationDescs = NULL; - indexstate->iss_ScanDescs = NULL; - - node->indxstate = indexstate; - - /* ---------------- - * assign base id to index scan state also - * ---------------- - */ - indexstate->cstate.cs_base_id = baseid; - baseid++; - estate->es_BaseId = baseid; - - /* ---------------- - * get the index node information - * ---------------- - */ - indxid = node->indxid; - indxqual = node->indxqual; - numIndices = length(indxid); - indexPtr = 0; - - CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext); - - /* ---------------- - * scanKeys is used to keep track of the ScanKey's. This is needed - * because a single scan may use several indices and each index has - * its own ScanKey. - * ---------------- - */ - numScanKeys = (int *) palloc(numIndices * sizeof(int)); - scanKeys = (ScanKey *) palloc(numIndices * sizeof(ScanKey)); - relationDescs = (RelationPtr) palloc(numIndices * sizeof(Relation)); - scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc)); - - /* ---------------- - * initialize runtime key info. - * ---------------- - */ - have_runtime_keys = false; - runtimeKeyInfo = (Pointer *) - palloc(numIndices * sizeof(Pointer)); - - /* ---------------- - * build the index scan keys from the index qualification - * ---------------- - */ - for (i=0; i < numIndices; i++) { - int j; - List *qual; - int n_keys; - ScanKey scan_keys; - int *run_keys; - - qual = nth(i, indxqual); - n_keys = length(qual); - scan_keys = (n_keys <= 0) ? NULL : - (ScanKey)palloc(n_keys * sizeof(ScanKeyData)); - run_keys = (n_keys <= 0) ? NULL : - (int *)palloc(n_keys * sizeof(int)); - - CXT1_printf("ExecInitIndexScan: context is %d\n", - CurrentMemoryContext); - /* ---------------- - * for each opclause in the given qual, - * convert each qual's opclause into a single scan key + * tuple table initialization * ---------------- */ - for (j=0; j < n_keys; j++) { - Expr *clause; /* one part of index qual */ - Oper *op; /* operator used in scan.. */ - Node *leftop; /* expr on lhs of operator */ - Node *rightop; /* expr on rhs ... */ - bits16 flags = 0; - - int scanvar; /* which var identifies varattno */ - AttrNumber varattno = 0; /* att number used in scan */ - Oid opid; /* operator id used in scan */ - Datum scanvalue = 0; /* value used in scan (if const) */ - - /* ---------------- - * extract clause information from the qualification - * ---------------- - */ - clause = nth(j, qual); - - op = (Oper*)clause->oper; - if (!IsA(op,Oper)) - elog(WARN, "ExecInitIndexScan: op not an Oper!"); - - opid = op->opid; - - /* ---------------- - * Here we figure out the contents of the index qual. - * The usual case is (op var const) or (op const var) - * which means we form a scan key for the attribute - * listed in the var node and use the value of the const. - * - * If we don't have a const node, then it means that - * one of the var nodes refers to the "scan" tuple and - * is used to determine which attribute to scan, and the - * other expression is used to calculate the value used in - * scanning the index. - * - * This means our index scan's scan key is a function of - * information obtained during the execution of the plan - * in which case we need to recalculate the index scan key - * at run time. - * - * Hence, we set have_runtime_keys to true and then set - * the appropriate flag in run_keys to LEFT_OP or RIGHT_OP. - * The corresponding scan keys are recomputed at run time. - * ---------------- - */ - - scanvar = NO_OP; - - /* ---------------- - * determine information in leftop - * ---------------- - */ - leftop = (Node*) get_leftop(clause); - - if (IsA(leftop,Var) && var_is_rel((Var*)leftop)) { - /* ---------------- - * if the leftop is a "rel-var", then it means - * that it is a var node which tells us which - * attribute to use for our scan key. - * ---------------- - */ - varattno = ((Var*) leftop)->varattno; - scanvar = LEFT_OP; - } else if (IsA(leftop,Const)) { + ExecInitResultTupleSlot(estate, &scanstate->cstate); + ExecInitScanTupleSlot(estate, scanstate); +/* ExecInitRawTupleSlot(estate, scanstate); */ + + /* ---------------- + * initialize projection info. result type comes from scan desc + * below.. + * ---------------- + */ + ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate); + + /* -------------------------------- + * Part 2) initialize index scan state + * + * create new IndexScanState for node + * -------------------------------- + */ + indexstate = makeNode(IndexScanState); + indexstate->iss_NumIndices = 0; + indexstate->iss_IndexPtr = 0; + indexstate->iss_ScanKeys = NULL; + indexstate->iss_NumScanKeys = NULL; + indexstate->iss_RuntimeKeyInfo = NULL; + indexstate->iss_RelationDescs = NULL; + indexstate->iss_ScanDescs = NULL; + + node->indxstate = indexstate; + + /* ---------------- + * assign base id to index scan state also + * ---------------- + */ + indexstate->cstate.cs_base_id = baseid; + baseid++; + estate->es_BaseId = baseid; + + /* ---------------- + * get the index node information + * ---------------- + */ + indxid = node->indxid; + indxqual = node->indxqual; + numIndices = length(indxid); + indexPtr = 0; + + CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext); + + /* ---------------- + * scanKeys is used to keep track of the ScanKey's. This is needed + * because a single scan may use several indices and each index has + * its own ScanKey. + * ---------------- + */ + numScanKeys = (int *) palloc(numIndices * sizeof(int)); + scanKeys = (ScanKey *) palloc(numIndices * sizeof(ScanKey)); + relationDescs = (RelationPtr) palloc(numIndices * sizeof(Relation)); + scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc)); + + /* ---------------- + * initialize runtime key info. + * ---------------- + */ + have_runtime_keys = false; + runtimeKeyInfo = (Pointer *) + palloc(numIndices * sizeof(Pointer)); + + /* ---------------- + * build the index scan keys from the index qualification + * ---------------- + */ + for (i = 0; i < numIndices; i++) + { + int j; + List *qual; + int n_keys; + ScanKey scan_keys; + int *run_keys; + + qual = nth(i, indxqual); + n_keys = length(qual); + scan_keys = (n_keys <= 0) ? NULL : + (ScanKey) palloc(n_keys * sizeof(ScanKeyData)); + run_keys = (n_keys <= 0) ? NULL : + (int *) palloc(n_keys * sizeof(int)); + + CXT1_printf("ExecInitIndexScan: context is %d\n", + CurrentMemoryContext); + /* ---------------- - * if the leftop is a const node then it means - * it identifies the value to place in our scan key. + * for each opclause in the given qual, + * convert each qual's opclause into a single scan key * ---------------- */ - run_keys[ j ] = NO_OP; - scanvalue = ((Const*) leftop)->constvalue; + for (j = 0; j < n_keys; j++) + { + Expr *clause; /* one part of index qual */ + Oper *op; /* operator used in scan.. */ + Node *leftop; /* expr on lhs of operator */ + Node *rightop; /* expr on rhs ... */ + bits16 flags = 0; + + int scanvar; /* which var identifies varattno */ + AttrNumber varattno = 0; /* att number used in scan */ + Oid opid; /* operator id used in scan */ + Datum scanvalue = 0; /* value used in scan (if + * const) */ + + /* ---------------- + * extract clause information from the qualification + * ---------------- + */ + clause = nth(j, qual); + + op = (Oper *) clause->oper; + if (!IsA(op, Oper)) + elog(WARN, "ExecInitIndexScan: op not an Oper!"); + + opid = op->opid; + + /* ---------------- + * Here we figure out the contents of the index qual. + * The usual case is (op var const) or (op const var) + * which means we form a scan key for the attribute + * listed in the var node and use the value of the const. + * + * If we don't have a const node, then it means that + * one of the var nodes refers to the "scan" tuple and + * is used to determine which attribute to scan, and the + * other expression is used to calculate the value used in + * scanning the index. + * + * This means our index scan's scan key is a function of + * information obtained during the execution of the plan + * in which case we need to recalculate the index scan key + * at run time. + * + * Hence, we set have_runtime_keys to true and then set + * the appropriate flag in run_keys to LEFT_OP or RIGHT_OP. + * The corresponding scan keys are recomputed at run time. + * ---------------- + */ + + scanvar = NO_OP; + + /* ---------------- + * determine information in leftop + * ---------------- + */ + leftop = (Node *) get_leftop(clause); + + if (IsA(leftop, Var) && var_is_rel((Var *) leftop)) + { + /* ---------------- + * if the leftop is a "rel-var", then it means + * that it is a var node which tells us which + * attribute to use for our scan key. + * ---------------- + */ + varattno = ((Var *) leftop)->varattno; + scanvar = LEFT_OP; + } + else if (IsA(leftop, Const)) + { + /* ---------------- + * if the leftop is a const node then it means + * it identifies the value to place in our scan key. + * ---------------- + */ + run_keys[j] = NO_OP; + scanvalue = ((Const *) leftop)->constvalue; #ifdef INDEXSCAN_PATCH - } else if (IsA(leftop,Param)) { - bool isnull; - /* ---------------- - * if the leftop is a Param node then it means - * it identifies the value to place in our scan key. - * ---------------- - */ - run_keys[ j ] = NO_OP; - scanvalue = ExecEvalParam((Param*) leftop, - scanstate->cstate.cs_ExprContext, - &isnull); - if ( isnull ) - flags |= SK_ISNULL; + } + else if (IsA(leftop, Param)) + { + bool isnull; + + /* ---------------- + * if the leftop is a Param node then it means + * it identifies the value to place in our scan key. + * ---------------- + */ + run_keys[j] = NO_OP; + scanvalue = ExecEvalParam((Param *) leftop, + scanstate->cstate.cs_ExprContext, + &isnull); + if (isnull) + flags |= SK_ISNULL; #endif - } else if (leftop != NULL && - is_funcclause(leftop) && - var_is_rel(lfirst(((Expr*)leftop)->args))) { - /* ---------------- - * if the leftop is a func node then it means - * it identifies the value to place in our scan key. - * Since functional indices have only one attribute - * the attno must always be set to 1. - * ---------------- - */ - varattno = 1; - scanvar = LEFT_OP; - - } else { - /* ---------------- - * otherwise, the leftop contains information usable - * at runtime to figure out the value to place in our - * scan key. - * ---------------- - */ - have_runtime_keys = true; - run_keys[ j ] = LEFT_OP; - scanvalue = Int32GetDatum((int32) true); - } - - /* ---------------- - * now determine information in rightop - * ---------------- - */ - rightop = (Node*) get_rightop(clause); - - if (IsA(rightop,Var) && var_is_rel((Var*)rightop)) { - /* ---------------- - * here we make sure only one op identifies the - * scan-attribute... - * ---------------- - */ - if (scanvar == LEFT_OP) - elog(WARN, "ExecInitIndexScan: %s", - "both left and right op's are rel-vars"); - - /* ---------------- - * if the rightop is a "rel-var", then it means - * that it is a var node which tells us which - * attribute to use for our scan key. - * ---------------- - */ - varattno = ((Var*) rightop)->varattno; - scanvar = RIGHT_OP; - - } else if (IsA(rightop,Const)) { - /* ---------------- - * if the leftop is a const node then it means - * it identifies the value to place in our scan key. - * ---------------- - */ - run_keys[ j ] = NO_OP; - scanvalue = ((Const*) rightop)->constvalue; + } + else if (leftop != NULL && + is_funcclause(leftop) && + var_is_rel(lfirst(((Expr *) leftop)->args))) + { + /* ---------------- + * if the leftop is a func node then it means + * it identifies the value to place in our scan key. + * Since functional indices have only one attribute + * the attno must always be set to 1. + * ---------------- + */ + varattno = 1; + scanvar = LEFT_OP; + + } + else + { + /* ---------------- + * otherwise, the leftop contains information usable + * at runtime to figure out the value to place in our + * scan key. + * ---------------- + */ + have_runtime_keys = true; + run_keys[j] = LEFT_OP; + scanvalue = Int32GetDatum((int32) true); + } + + /* ---------------- + * now determine information in rightop + * ---------------- + */ + rightop = (Node *) get_rightop(clause); + + if (IsA(rightop, Var) && var_is_rel((Var *) rightop)) + { + /* ---------------- + * here we make sure only one op identifies the + * scan-attribute... + * ---------------- + */ + if (scanvar == LEFT_OP) + elog(WARN, "ExecInitIndexScan: %s", + "both left and right op's are rel-vars"); + + /* ---------------- + * if the rightop is a "rel-var", then it means + * that it is a var node which tells us which + * attribute to use for our scan key. + * ---------------- + */ + varattno = ((Var *) rightop)->varattno; + scanvar = RIGHT_OP; + + } + else if (IsA(rightop, Const)) + { + /* ---------------- + * if the leftop is a const node then it means + * it identifies the value to place in our scan key. + * ---------------- + */ + run_keys[j] = NO_OP; + scanvalue = ((Const *) rightop)->constvalue; #ifdef INDEXSCAN_PATCH - } else if (IsA(rightop,Param)) { - bool isnull; - /* ---------------- - * if the rightop is a Param node then it means - * it identifies the value to place in our scan key. - * ---------------- - */ - run_keys[ j ] = NO_OP; - scanvalue = ExecEvalParam((Param*) rightop, - scanstate->cstate.cs_ExprContext, - &isnull); - if ( isnull ) - flags |= SK_ISNULL; + } + else if (IsA(rightop, Param)) + { + bool isnull; + + /* ---------------- + * if the rightop is a Param node then it means + * it identifies the value to place in our scan key. + * ---------------- + */ + run_keys[j] = NO_OP; + scanvalue = ExecEvalParam((Param *) rightop, + scanstate->cstate.cs_ExprContext, + &isnull); + if (isnull) + flags |= SK_ISNULL; #endif - } else if (rightop!=NULL && - is_funcclause(rightop) && - var_is_rel(lfirst(((Expr*)rightop)->args))) { - /* ---------------- - * if the rightop is a func node then it means - * it identifies the value to place in our scan key. - * Since functional indices have only one attribute - * the attno must always be set to 1. - * ---------------- - */ - if (scanvar == LEFT_OP) - elog(WARN, "ExecInitIndexScan: %s", - "both left and right ops are rel-vars"); - - varattno = 1; - scanvar = RIGHT_OP; - - } else { + } + else if (rightop != NULL && + is_funcclause(rightop) && + var_is_rel(lfirst(((Expr *) rightop)->args))) + { + /* ---------------- + * if the rightop is a func node then it means + * it identifies the value to place in our scan key. + * Since functional indices have only one attribute + * the attno must always be set to 1. + * ---------------- + */ + if (scanvar == LEFT_OP) + elog(WARN, "ExecInitIndexScan: %s", + "both left and right ops are rel-vars"); + + varattno = 1; + scanvar = RIGHT_OP; + + } + else + { + /* ---------------- + * otherwise, the leftop contains information usable + * at runtime to figure out the value to place in our + * scan key. + * ---------------- + */ + have_runtime_keys = true; + run_keys[j] = RIGHT_OP; + scanvalue = Int32GetDatum((int32) true); + } + + /* ---------------- + * now check that at least one op tells us the scan + * attribute... + * ---------------- + */ + if (scanvar == NO_OP) + elog(WARN, "ExecInitIndexScan: %s", + "neither leftop nor rightop refer to scan relation"); + + /* ---------------- + * initialize the scan key's fields appropriately + * ---------------- + */ + ScanKeyEntryInitialize(&scan_keys[j], + flags, + varattno, /* attribute number to + * scan */ + (RegProcedure) opid, /* reg proc to use */ + (Datum) scanvalue); /* constant */ + } + /* ---------------- - * otherwise, the leftop contains information usable - * at runtime to figure out the value to place in our - * scan key. + * store the key information into our array. * ---------------- */ - have_runtime_keys = true; - run_keys[ j ] = RIGHT_OP; - scanvalue = Int32GetDatum((int32) true); - } - - /* ---------------- - * now check that at least one op tells us the scan - * attribute... - * ---------------- - */ - if (scanvar == NO_OP) - elog(WARN, "ExecInitIndexScan: %s", - "neither leftop nor rightop refer to scan relation"); - - /* ---------------- - * initialize the scan key's fields appropriately - * ---------------- - */ - ScanKeyEntryInitialize(&scan_keys[j], - flags, - varattno, /* attribute number to scan */ - (RegProcedure) opid, /* reg proc to use */ - (Datum) scanvalue); /* constant */ + numScanKeys[i] = n_keys; + scanKeys[i] = scan_keys; + runtimeKeyInfo[i] = (Pointer) run_keys; } - + + indexstate->iss_NumIndices = numIndices; + indexstate->iss_IndexPtr = indexPtr; + indexstate->iss_ScanKeys = scanKeys; + indexstate->iss_NumScanKeys = numScanKeys; + /* ---------------- - * store the key information into our array. + * If all of our keys have the form (op var const) , then we have no + * runtime keys so we store NULL in the runtime key info. + * Otherwise runtime key info contains an array of pointers + * (one for each index) to arrays of flags (one for each key) + * which indicate that the qual needs to be evaluated at runtime. + * -cim 10/24/89 * ---------------- */ - numScanKeys[ i ] = n_keys; - scanKeys[ i ] = scan_keys; - runtimeKeyInfo[ i ] = (Pointer) run_keys; - } - - indexstate->iss_NumIndices = numIndices; - indexstate->iss_IndexPtr = indexPtr; - indexstate->iss_ScanKeys = scanKeys; - indexstate->iss_NumScanKeys = numScanKeys; - - /* ---------------- - * If all of our keys have the form (op var const) , then we have no - * runtime keys so we store NULL in the runtime key info. - * Otherwise runtime key info contains an array of pointers - * (one for each index) to arrays of flags (one for each key) - * which indicate that the qual needs to be evaluated at runtime. - * -cim 10/24/89 - * ---------------- - */ - if (have_runtime_keys) + if (have_runtime_keys) { - indexstate->iss_RuntimeKeyInfo = (Pointer) runtimeKeyInfo; + indexstate->iss_RuntimeKeyInfo = (Pointer) runtimeKeyInfo; } - else { - indexstate->iss_RuntimeKeyInfo = NULL; - for (i=0; i < numIndices; i++) { - List *qual; - int n_keys; - qual = nth(i, indxqual); - n_keys = length(qual); - if (n_keys > 0) - pfree(runtimeKeyInfo[i]); + else + { + indexstate->iss_RuntimeKeyInfo = NULL; + for (i = 0; i < numIndices; i++) + { + List *qual; + int n_keys; + + qual = nth(i, indxqual); + n_keys = length(qual); + if (n_keys > 0) + pfree(runtimeKeyInfo[i]); + } + pfree(runtimeKeyInfo); } - pfree(runtimeKeyInfo); - } - - /* ---------------- - * get the range table and direction information - * from the execution state (these are needed to - * open the relations). - * ---------------- - */ - rangeTable = estate->es_range_table; - direction = estate->es_direction; - - /* ---------------- - * open the base relation - * ---------------- - */ - relid = node->scan.scanrelid; - rtentry = rt_fetch(relid, rangeTable); - reloid = rtentry->relid; - timeQual = rtentry->timeQual; - - ExecOpenScanR(reloid, /* relation */ - 0, /* nkeys */ - (ScanKey) NULL, /* scan key */ - 0, /* is index */ - direction, /* scan direction */ - timeQual, /* time qual */ - ¤tRelation, /* return: rel desc */ - (Pointer *) ¤tScanDesc); /* return: scan desc */ - - scanstate->css_currentRelation = currentRelation; - scanstate->css_currentScanDesc = currentScanDesc; - - - /* ---------------- - * get the scan type from the relation descriptor. - * ---------------- - */ - ExecAssignScanType(scanstate, RelationGetTupleDescriptor(currentRelation)); - ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate); - - /* ---------------- - * index scans don't have subtrees.. - * ---------------- - */ -/* scanstate->ss_ProcOuterFlag = false; */ - - /* ---------------- - * open the index relations and initialize - * relation and scan descriptors. - * ---------------- - */ - for (i=0; i < numIndices; i++) { - Oid indexOid; - - indexOid = (Oid)nthi(i, indxid); - - if (indexOid != 0) { - ExecOpenScanR(indexOid, /* relation */ - numScanKeys[ i ], /* nkeys */ - scanKeys[ i ], /* scan key */ - true, /* is index */ - direction, /* scan direction */ - timeQual, /* time qual */ - &(relationDescs[ i ]), /* return: rel desc */ - (Pointer *) &(scanDescs[ i ])); - /* return: scan desc */ + + /* ---------------- + * get the range table and direction information + * from the execution state (these are needed to + * open the relations). + * ---------------- + */ + rangeTable = estate->es_range_table; + direction = estate->es_direction; + + /* ---------------- + * open the base relation + * ---------------- + */ + relid = node->scan.scanrelid; + rtentry = rt_fetch(relid, rangeTable); + reloid = rtentry->relid; + timeQual = rtentry->timeQual; + + ExecOpenScanR(reloid, /* relation */ + 0, /* nkeys */ + (ScanKey) NULL, /* scan key */ + 0, /* is index */ + direction, /* scan direction */ + timeQual, /* time qual */ + ¤tRelation, /* return: rel desc */ + (Pointer *) & currentScanDesc); /* return: scan desc */ + + scanstate->css_currentRelation = currentRelation; + scanstate->css_currentScanDesc = currentScanDesc; + + + /* ---------------- + * get the scan type from the relation descriptor. + * ---------------- + */ + ExecAssignScanType(scanstate, RelationGetTupleDescriptor(currentRelation)); + ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate); + + /* ---------------- + * index scans don't have subtrees.. + * ---------------- + */ +/* scanstate->ss_ProcOuterFlag = false; */ + + /* ---------------- + * open the index relations and initialize + * relation and scan descriptors. + * ---------------- + */ + for (i = 0; i < numIndices; i++) + { + Oid indexOid; + + indexOid = (Oid) nthi(i, indxid); + + if (indexOid != 0) + { + ExecOpenScanR(indexOid, /* relation */ + numScanKeys[i], /* nkeys */ + scanKeys[i], /* scan key */ + true, /* is index */ + direction, /* scan direction */ + timeQual, /* time qual */ + &(relationDescs[i]), /* return: rel desc */ + (Pointer *) & (scanDescs[i])); + /* return: scan desc */ + } } - } - indexstate->iss_RelationDescs = relationDescs; - indexstate->iss_ScanDescs = scanDescs; + indexstate->iss_RelationDescs = relationDescs; + indexstate->iss_ScanDescs = scanDescs; - indexstate->cstate.cs_TupFromTlist = false; + indexstate->cstate.cs_TupFromTlist = false; - /* ---------------- - * all done. - * ---------------- - */ - return TRUE; + /* ---------------- + * all done. + * ---------------- + */ + return TRUE; } int -ExecCountSlotsIndexScan(IndexScan *node) +ExecCountSlotsIndexScan(IndexScan * node) { - return ExecCountSlotsNode(outerPlan((Plan *)node)) + - ExecCountSlotsNode(innerPlan((Plan *)node)) + - INDEXSCAN_NSLOTS; + return ExecCountSlotsNode(outerPlan((Plan *) node)) + + ExecCountSlotsNode(innerPlan((Plan *) node)) + + INDEXSCAN_NSLOTS; } diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c index daf324bb77a..49ba73d3bf0 100644 --- a/src/backend/executor/nodeMaterial.c +++ b/src/backend/executor/nodeMaterial.c @@ -1,21 +1,21 @@ /*------------------------------------------------------------------------- * * nodeMaterial.c-- - * Routines to handle materialization nodes. + * Routines to handle materialization nodes. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.6 1997/08/20 14:53:24 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.7 1997/09/07 04:41:36 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * ExecMaterial - generate a temporary relation - * ExecInitMaterial - initialize node and subnodes.. - * ExecEndMaterial - shutdown node and subnodes + * ExecMaterial - generate a temporary relation + * ExecInitMaterial - initialize node and subnodes.. + * ExecEndMaterial - shutdown node and subnodes * */ #include "postgres.h" @@ -29,368 +29,373 @@ #include "access/heapam.h" /* ---------------------------------------------------------------- - * ExecMaterial + * ExecMaterial * - * The first time this is called, ExecMaterial retrieves tuples - * this node's outer subplan and inserts them into a temporary - * relation. After this is done, a flag is set indicating that - * the subplan has been materialized. Once the relation is - * materialized, the first tuple is then returned. Successive - * calls to ExecMaterial return successive tuples from the temp - * relation. + * The first time this is called, ExecMaterial retrieves tuples + * this node's outer subplan and inserts them into a temporary + * relation. After this is done, a flag is set indicating that + * the subplan has been materialized. Once the relation is + * materialized, the first tuple is then returned. Successive + * calls to ExecMaterial return successive tuples from the temp + * relation. * - * Initial State: + * Initial State: * - * ExecMaterial assumes the temporary relation has been - * created and openend by ExecInitMaterial during the prior - * InitPlan() phase. + * ExecMaterial assumes the temporary relation has been + * created and openend by ExecInitMaterial during the prior + * InitPlan() phase. * * ---------------------------------------------------------------- */ -TupleTableSlot * /* result tuple from subplan */ -ExecMaterial(Material *node) +TupleTableSlot * /* result tuple from subplan */ +ExecMaterial(Material * node) { - EState *estate; - MaterialState *matstate; - Plan *outerNode; - ScanDirection dir; - Relation tempRelation; - Relation currentRelation; - HeapScanDesc currentScanDesc; - HeapTuple heapTuple; - TupleTableSlot *slot; - Buffer buffer; - - /* ---------------- - * get state info from node - * ---------------- - */ - matstate = node->matstate; - estate = node->plan.state; - dir = estate->es_direction; - - /* ---------------- - * the first time we call this, we retrieve all tuples - * from the subplan into a temporary relation and then - * we sort the relation. Subsequent calls return tuples - * from the temporary relation. - * ---------------- - */ - - if (matstate->mat_Flag == false) { + EState *estate; + MaterialState *matstate; + Plan *outerNode; + ScanDirection dir; + Relation tempRelation; + Relation currentRelation; + HeapScanDesc currentScanDesc; + HeapTuple heapTuple; + TupleTableSlot *slot; + Buffer buffer; + /* ---------------- - * set all relations to be scanned in the forward direction - * while creating the temporary relation. + * get state info from node * ---------------- */ - estate->es_direction = ForwardScanDirection; - + matstate = node->matstate; + estate = node->plan.state; + dir = estate->es_direction; + /* ---------------- - * if we couldn't create the temp or current relations then - * we print a warning and return NULL. + * the first time we call this, we retrieve all tuples + * from the subplan into a temporary relation and then + * we sort the relation. Subsequent calls return tuples + * from the temporary relation. * ---------------- */ - tempRelation = matstate->mat_TempRelation; - if (tempRelation == NULL) { - elog(DEBUG, "ExecMaterial: temp relation is NULL! aborting..."); - return NULL; - } - - currentRelation = matstate->csstate.css_currentRelation; - if (currentRelation == NULL) { - elog(DEBUG, "ExecMaterial: current relation is NULL! aborting..."); - return NULL; + + if (matstate->mat_Flag == false) + { + /* ---------------- + * set all relations to be scanned in the forward direction + * while creating the temporary relation. + * ---------------- + */ + estate->es_direction = ForwardScanDirection; + + /* ---------------- + * if we couldn't create the temp or current relations then + * we print a warning and return NULL. + * ---------------- + */ + tempRelation = matstate->mat_TempRelation; + if (tempRelation == NULL) + { + elog(DEBUG, "ExecMaterial: temp relation is NULL! aborting..."); + return NULL; + } + + currentRelation = matstate->csstate.css_currentRelation; + if (currentRelation == NULL) + { + elog(DEBUG, "ExecMaterial: current relation is NULL! aborting..."); + return NULL; + } + + /* ---------------- + * retrieve tuples from the subplan and + * insert them in the temporary relation + * ---------------- + */ + outerNode = outerPlan((Plan *) node); + for (;;) + { + slot = ExecProcNode(outerNode, (Plan *) node); + + heapTuple = slot->val; + if (heapTuple == NULL) + break; + + heap_insert(tempRelation, /* relation desc */ + heapTuple); /* heap tuple to insert */ + + ExecClearTuple(slot); + } + currentRelation = tempRelation; + + /* ---------------- + * restore to user specified direction + * ---------------- + */ + estate->es_direction = dir; + + /* ---------------- + * now initialize the scan descriptor to scan the + * sorted relation and update the sortstate information + * ---------------- + */ + currentScanDesc = heap_beginscan(currentRelation, /* relation */ + ScanDirectionIsBackward(dir), + /* bkwd flag */ + NowTimeQual, /* time qual */ + 0, /* num scan keys */ + NULL); /* scan keys */ + matstate->csstate.css_currentRelation = currentRelation; + matstate->csstate.css_currentScanDesc = currentScanDesc; + + ExecAssignScanType(&matstate->csstate, + RelationGetTupleDescriptor(currentRelation)); + + /* ---------------- + * finally set the sorted flag to true + * ---------------- + */ + matstate->mat_Flag = true; } - + /* ---------------- - * retrieve tuples from the subplan and - * insert them in the temporary relation + * at this point we know we have a sorted relation so + * we preform a simple scan on it with amgetnext().. * ---------------- */ - outerNode = outerPlan((Plan *) node); - for (;;) { - slot = ExecProcNode(outerNode, (Plan*) node); - - heapTuple = slot->val; - if (heapTuple == NULL) - break; - - heap_insert(tempRelation, /* relation desc */ - heapTuple); /* heap tuple to insert */ - - ExecClearTuple( slot); - } - currentRelation = tempRelation; - + currentScanDesc = matstate->csstate.css_currentScanDesc; + + heapTuple = heap_getnext(currentScanDesc, /* scan desc */ + ScanDirectionIsBackward(dir), + /* bkwd flag */ + &buffer); /* return: buffer */ + /* ---------------- - * restore to user specified direction + * put the tuple into the scan tuple slot and return the slot. + * Note: since the tuple is really a pointer to a page, we don't want + * to call pfree() on it.. * ---------------- */ - estate->es_direction = dir; - + slot = (TupleTableSlot *) matstate->csstate.css_ScanTupleSlot; + + return ExecStoreTuple(heapTuple, /* tuple to store */ + slot, /* slot to store in */ + buffer, /* buffer for this tuple */ + false); /* don't pfree this pointer */ + +} + +/* ---------------------------------------------------------------- + * ExecInitMaterial + * ---------------------------------------------------------------- + */ +bool /* initialization status */ +ExecInitMaterial(Material * node, EState * estate, Plan * parent) +{ + MaterialState *matstate; + Plan *outerPlan; + TupleDesc tupType; + Relation tempDesc; + + /* int len; */ + /* ---------------- - * now initialize the scan descriptor to scan the - * sorted relation and update the sortstate information + * assign the node's execution state * ---------------- */ - currentScanDesc = heap_beginscan(currentRelation, /* relation */ - ScanDirectionIsBackward(dir), - /* bkwd flag */ - NowTimeQual, /* time qual */ - 0, /* num scan keys */ - NULL); /* scan keys */ - matstate->csstate.css_currentRelation = currentRelation; - matstate->csstate.css_currentScanDesc = currentScanDesc; - - ExecAssignScanType(&matstate->csstate, - RelationGetTupleDescriptor(currentRelation)); - + node->plan.state = estate; + /* ---------------- - * finally set the sorted flag to true + * create state structure * ---------------- */ - matstate->mat_Flag = true; - } - - /* ---------------- - * at this point we know we have a sorted relation so - * we preform a simple scan on it with amgetnext().. - * ---------------- - */ - currentScanDesc = matstate->csstate.css_currentScanDesc; - - heapTuple = heap_getnext(currentScanDesc, /* scan desc */ - ScanDirectionIsBackward(dir), - /* bkwd flag */ - &buffer); /* return: buffer */ - - /* ---------------- - * put the tuple into the scan tuple slot and return the slot. - * Note: since the tuple is really a pointer to a page, we don't want - * to call pfree() on it.. - * ---------------- - */ - slot = (TupleTableSlot *)matstate->csstate.css_ScanTupleSlot; - - return ExecStoreTuple(heapTuple, /* tuple to store */ - slot, /* slot to store in */ - buffer, /* buffer for this tuple */ - false); /* don't pfree this pointer */ - -} + matstate = makeNode(MaterialState); + matstate->mat_Flag = false; + matstate->mat_TempRelation = NULL; + node->matstate = matstate; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks and + * + assign result tuple slot + * + * Materialization nodes don't need ExprContexts because + * they never call ExecQual or ExecTargetList. + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &matstate->csstate.cstate, parent); -/* ---------------------------------------------------------------- - * ExecInitMaterial - * ---------------------------------------------------------------- - */ -bool /* initialization status */ -ExecInitMaterial(Material *node, EState *estate, Plan *parent) -{ - MaterialState *matstate; - Plan *outerPlan; - TupleDesc tupType; - Relation tempDesc; - /* int len; */ - - /* ---------------- - * assign the node's execution state - * ---------------- - */ - node->plan.state = estate; - - /* ---------------- - * create state structure - * ---------------- - */ - matstate = makeNode(MaterialState); - matstate->mat_Flag = false; - matstate->mat_TempRelation = NULL; - node->matstate = matstate; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks and - * + assign result tuple slot - * - * Materialization nodes don't need ExprContexts because - * they never call ExecQual or ExecTargetList. - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &matstate->csstate.cstate, parent); - #define MATERIAL_NSLOTS 1 - /* ---------------- - * tuple table initialization - * ---------------- - */ - ExecInitScanTupleSlot(estate, &matstate->csstate); - - /* ---------------- - * initializes child nodes - * ---------------- - */ - outerPlan = outerPlan((Plan *) node); - ExecInitNode(outerPlan, estate, (Plan *) node); - - /* ---------------- - * initialize matstate information - * ---------------- - */ - matstate->mat_Flag = false; - - /* ---------------- - * initialize tuple type. no need to initialize projection - * info because this node doesn't do projections. - * ---------------- - */ - ExecAssignScanTypeFromOuterPlan((Plan *) node, &matstate->csstate); - matstate->csstate.cstate.cs_ProjInfo = NULL; - - /* ---------------- - * get type information needed for ExecCreatR - * ---------------- - */ - tupType = ExecGetScanType(&matstate->csstate); - - /* ---------------- - * ExecCreatR wants it's second argument to be an object id of - * a relation in the range table or a _TEMP_RELATION_ID - * indicating that the relation is not in the range table. - * - * In the second case ExecCreatR creates a temp relation. - * (currently this is the only case we support -cim 10/16/89) - * ---------------- - */ - /* ---------------- - * create the temporary relation - * ---------------- - */ -/* len = ExecTargetListLength(node->plan.targetlist); */ - tempDesc = ExecCreatR(tupType, _TEMP_RELATION_ID_); - - /* ---------------- - * save the relation descriptor in the sortstate - * ---------------- - */ - matstate->mat_TempRelation = tempDesc; - matstate->csstate.css_currentRelation = tempDesc; - - /* ---------------- - * return relation oid of temporary relation in a list - * (someday -- for now we return LispTrue... cim 10/12/89) - * ---------------- - */ - return TRUE; + /* ---------------- + * tuple table initialization + * ---------------- + */ + ExecInitScanTupleSlot(estate, &matstate->csstate); + + /* ---------------- + * initializes child nodes + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + ExecInitNode(outerPlan, estate, (Plan *) node); + + /* ---------------- + * initialize matstate information + * ---------------- + */ + matstate->mat_Flag = false; + + /* ---------------- + * initialize tuple type. no need to initialize projection + * info because this node doesn't do projections. + * ---------------- + */ + ExecAssignScanTypeFromOuterPlan((Plan *) node, &matstate->csstate); + matstate->csstate.cstate.cs_ProjInfo = NULL; + + /* ---------------- + * get type information needed for ExecCreatR + * ---------------- + */ + tupType = ExecGetScanType(&matstate->csstate); + + /* ---------------- + * ExecCreatR wants it's second argument to be an object id of + * a relation in the range table or a _TEMP_RELATION_ID + * indicating that the relation is not in the range table. + * + * In the second case ExecCreatR creates a temp relation. + * (currently this is the only case we support -cim 10/16/89) + * ---------------- + */ + /* ---------------- + * create the temporary relation + * ---------------- + */ +/* len = ExecTargetListLength(node->plan.targetlist); */ + tempDesc = ExecCreatR(tupType, _TEMP_RELATION_ID_); + + /* ---------------- + * save the relation descriptor in the sortstate + * ---------------- + */ + matstate->mat_TempRelation = tempDesc; + matstate->csstate.css_currentRelation = tempDesc; + + /* ---------------- + * return relation oid of temporary relation in a list + * (someday -- for now we return LispTrue... cim 10/12/89) + * ---------------- + */ + return TRUE; } int -ExecCountSlotsMaterial(Material *node) +ExecCountSlotsMaterial(Material * node) { - return ExecCountSlotsNode(outerPlan((Plan *)node)) + - ExecCountSlotsNode(innerPlan((Plan *)node)) + - MATERIAL_NSLOTS; + return ExecCountSlotsNode(outerPlan((Plan *) node)) + + ExecCountSlotsNode(innerPlan((Plan *) node)) + + MATERIAL_NSLOTS; } /* ---------------------------------------------------------------- - * ExecEndMaterial + * ExecEndMaterial * * old comments - * destroys the temporary relation. + * destroys the temporary relation. * ---------------------------------------------------------------- */ void -ExecEndMaterial(Material *node) +ExecEndMaterial(Material * node) { - MaterialState *matstate; - Relation tempRelation; - Plan *outerPlan; - - /* ---------------- - * get info from the material state - * ---------------- - */ - matstate = node->matstate; - tempRelation = matstate->mat_TempRelation; - - heap_destroyr(tempRelation); - - /* ---------------- - * close the temp relation and shut down the scan. - * ---------------- - */ - ExecCloseR((Plan *) node); - - /* ---------------- - * shut down the subplan - * ---------------- - */ - outerPlan = outerPlan((Plan *) node); - ExecEndNode(outerPlan, (Plan*) node); - - /* ---------------- - * clean out the tuple table - * ---------------- - */ - ExecClearTuple(matstate->csstate.css_ScanTupleSlot); -} - -#ifdef NOT_USED /* not used */ + MaterialState *matstate; + Relation tempRelation; + Plan *outerPlan; + + /* ---------------- + * get info from the material state + * ---------------- + */ + matstate = node->matstate; + tempRelation = matstate->mat_TempRelation; + + heap_destroyr(tempRelation); + + /* ---------------- + * close the temp relation and shut down the scan. + * ---------------- + */ + ExecCloseR((Plan *) node); + + /* ---------------- + * shut down the subplan + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + ExecEndNode(outerPlan, (Plan *) node); + + /* ---------------- + * clean out the tuple table + * ---------------- + */ + ExecClearTuple(matstate->csstate.css_ScanTupleSlot); +} + +#ifdef NOT_USED /* not used */ /* ---------------------------------------------------------------- - * ExecMaterialMarkPos + * ExecMaterialMarkPos * ---------------------------------------------------------------- */ -List /* nothing of interest */ +List /* nothing of interest */ ExecMaterialMarkPos(Material node) { - MaterialState matstate; - HeapScanDesc sdesc; - - /* ---------------- - * if we haven't materialized yet, just return NIL. - * ---------------- - */ - matstate = get_matstate(node); - if (get_mat_Flag(matstate) == false) + MaterialState matstate; + HeapScanDesc sdesc; + + /* ---------------- + * if we haven't materialized yet, just return NIL. + * ---------------- + */ + matstate = get_matstate(node); + if (get_mat_Flag(matstate) == false) + return NIL; + + /* ---------------- + * XXX access methods don't return positions yet so + * for now we return NIL. It's possible that + * they will never return positions for all I know -cim 10/16/89 + * ---------------- + */ + sdesc = get_css_currentScanDesc((CommonScanState) matstate); + heap_markpos(sdesc); + return NIL; - - /* ---------------- - * XXX access methods don't return positions yet so - * for now we return NIL. It's possible that - * they will never return positions for all I know -cim 10/16/89 - * ---------------- - */ - sdesc = get_css_currentScanDesc((CommonScanState)matstate); - heap_markpos(sdesc); - - return NIL; } /* ---------------------------------------------------------------- - * ExecMaterialRestrPos + * ExecMaterialRestrPos * ---------------------------------------------------------------- */ void ExecMaterialRestrPos(Material node) { - MaterialState matstate; - HeapScanDesc sdesc; - - /* ---------------- - * if we haven't materialized yet, just return. - * ---------------- - */ - matstate = get_matstate(node); - if (get_mat_Flag(matstate) == false) - return; - - /* ---------------- - * restore the scan to the previously marked position - * ---------------- - */ - sdesc = get_css_currentScanDesc((CommonScanState)matstate); - heap_restrpos(sdesc); + MaterialState matstate; + HeapScanDesc sdesc; + + /* ---------------- + * if we haven't materialized yet, just return. + * ---------------- + */ + matstate = get_matstate(node); + if (get_mat_Flag(matstate) == false) + return; + + /* ---------------- + * restore the scan to the previously marked position + * ---------------- + */ + sdesc = get_css_currentScanDesc((CommonScanState) matstate); + heap_restrpos(sdesc); } -#endif +#endif diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 9151479d306..348d3fa1e00 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -1,78 +1,78 @@ /*------------------------------------------------------------------------- * * nodeMergejoin.c-- - * routines supporting merge joins + * routines supporting merge joins * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.8 1997/08/19 21:31:10 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.9 1997/09/07 04:41:37 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * ExecMergeJoin mergejoin outer and inner relations. - * ExecInitMergeJoin creates and initializes run time states - * ExecEndMergeJoin cleand up the node. + * ExecMergeJoin mergejoin outer and inner relations. + * ExecInitMergeJoin creates and initializes run time states + * ExecEndMergeJoin cleand up the node. * * NOTES - * Essential operation of the merge join algorithm is as follows: - * (** indicates the tuples satisify the merge clause). + * Essential operation of the merge join algorithm is as follows: + * (** indicates the tuples satisify the merge clause). * - * Join { - - * get initial outer and inner tuples INITIALIZE - * Skip Inner SKIPINNER - * mark inner position JOINMARK - * do forever { - - * while (outer ** inner) { JOINTEST - * join tuples JOINTUPLES - * advance inner position NEXTINNER - * } - - * advance outer position NEXTOUTER - * if (outer ** mark) { TESTOUTER - * restore inner position to mark TESTOUTER - * continue - - * } else { - - * Skip Outer SKIPOUTER - * mark inner position JOINMARK - * } - - * } - - * } - + * Join { - + * get initial outer and inner tuples INITIALIZE + * Skip Inner SKIPINNER + * mark inner position JOINMARK + * do forever { - + * while (outer ** inner) { JOINTEST + * join tuples JOINTUPLES + * advance inner position NEXTINNER + * } - + * advance outer position NEXTOUTER + * if (outer ** mark) { TESTOUTER + * restore inner position to mark TESTOUTER + * continue - + * } else { - + * Skip Outer SKIPOUTER + * mark inner position JOINMARK + * } - + * } - + * } - * - * Skip Outer { SKIPOUTER - * if (inner ** outer) Join Tuples JOINTUPLES - * while (outer < inner) SKIPOUTER - * advance outer SKIPOUTER - * if (outer > inner) SKIPOUTER - * Skip Inner SKIPINNER - * } - + * Skip Outer { SKIPOUTER + * if (inner ** outer) Join Tuples JOINTUPLES + * while (outer < inner) SKIPOUTER + * advance outer SKIPOUTER + * if (outer > inner) SKIPOUTER + * Skip Inner SKIPINNER + * } - * - * Skip Inner { SKIPINNER - * if (inner ** outer) Join Tuples JOINTUPLES - * while (outer > inner) SKIPINNER - * advance inner SKIPINNER - * if (outer < inner) SKIPINNER - * Skip Outer SKIPOUTER - * } - + * Skip Inner { SKIPINNER + * if (inner ** outer) Join Tuples JOINTUPLES + * while (outer > inner) SKIPINNER + * advance inner SKIPINNER + * if (outer < inner) SKIPINNER + * Skip Outer SKIPOUTER + * } - * - * Currently, the merge join operation is coded in the fashion - * of a state machine. At each state, we do something and then - * proceed to another state. This state is stored in the node's - * execution state information and is preserved across calls to - * ExecMergeJoin. -cim 10/31/89 + * Currently, the merge join operation is coded in the fashion + * of a state machine. At each state, we do something and then + * proceed to another state. This state is stored in the node's + * execution state information and is preserved across calls to + * ExecMergeJoin. -cim 10/31/89 * - * Warning: This code is known to fail for inequality operations - * and is being redesigned. Specifically, = and > work - * but the logic is not correct for <. Since mergejoins - * are no better then nestloops for inequalitys, the planner - * should not plan them anyways. Alternatively, the - * planner could just exchange the inner/outer relations - * if it ever sees a <... -cim 7/1/90 + * Warning: This code is known to fail for inequality operations + * and is being redesigned. Specifically, = and > work + * but the logic is not correct for <. Since mergejoins + * are no better then nestloops for inequalitys, the planner + * should not plan them anyways. Alternatively, the + * planner could just exchange the inner/outer relations + * if it ever sees a <... -cim 7/1/90 * - * Update: The executor tuple table has long since alleviated the - * problem described above -cim 4/23/91 + * Update: The executor tuple table has long since alleviated the + * problem described above -cim 4/23/91 * */ #include "postgres.h" @@ -84,1134 +84,1151 @@ #include "utils/lsyscache.h" #include "utils/psort.h" -static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext); +static bool MergeCompare(List * eqQual, List * compareQual, ExprContext * econtext); /* ---------------------------------------------------------------- - * MarkInnerTuple and RestoreInnerTuple macros + * MarkInnerTuple and RestoreInnerTuple macros * - * when we "mark" a tuple, we place a pointer to it - * in the marked tuple slot. now there are two pointers - * to this tuple and we don't want it to be freed until - * next time we mark a tuple, so we move the policy to - * the marked tuple slot and set the inner tuple slot policy - * to false. + * when we "mark" a tuple, we place a pointer to it + * in the marked tuple slot. now there are two pointers + * to this tuple and we don't want it to be freed until + * next time we mark a tuple, so we move the policy to + * the marked tuple slot and set the inner tuple slot policy + * to false. * - * But, when we restore the inner tuple, the marked tuple - * retains the policy. Basically once a tuple is marked, it - * should only be freed when we mark another tuple. -cim 9/27/90 + * But, when we restore the inner tuple, the marked tuple + * retains the policy. Basically once a tuple is marked, it + * should only be freed when we mark another tuple. -cim 9/27/90 * - * Note: now that we store buffers in the tuple table, - * we have to also increment buffer reference counts - * correctly whenever we propagate an additional pointer - * to a buffer item. Later, when ExecStoreTuple() is - * called again on this slot, the refcnt is decremented - * when the old tuple is replaced. + * Note: now that we store buffers in the tuple table, + * we have to also increment buffer reference counts + * correctly whenever we propagate an additional pointer + * to a buffer item. Later, when ExecStoreTuple() is + * called again on this slot, the refcnt is decremented + * when the old tuple is replaced. * ---------------------------------------------------------------- */ #define MarkInnerTuple(innerTupleSlot, mergestate) \ { \ - bool shouldFree; \ - shouldFree = ExecSetSlotPolicy(innerTupleSlot, false); \ - ExecStoreTuple(innerTupleSlot->val, \ - mergestate->mj_MarkedTupleSlot, \ - innerTupleSlot->ttc_buffer, \ - shouldFree); \ - ExecIncrSlotBufferRefcnt(innerTupleSlot); \ + bool shouldFree; \ + shouldFree = ExecSetSlotPolicy(innerTupleSlot, false); \ + ExecStoreTuple(innerTupleSlot->val, \ + mergestate->mj_MarkedTupleSlot, \ + innerTupleSlot->ttc_buffer, \ + shouldFree); \ + ExecIncrSlotBufferRefcnt(innerTupleSlot); \ } #define RestoreInnerTuple(innerTupleSlot, markedTupleSlot) \ - ExecStoreTuple(markedTupleSlot->val, \ - innerTupleSlot, \ - markedTupleSlot->ttc_buffer, \ - false); \ - ExecIncrSlotBufferRefcnt(innerTupleSlot) + ExecStoreTuple(markedTupleSlot->val, \ + innerTupleSlot, \ + markedTupleSlot->ttc_buffer, \ + false); \ + ExecIncrSlotBufferRefcnt(innerTupleSlot) /* ---------------------------------------------------------------- - * MJFormOSortopI + * MJFormOSortopI * - * This takes the mergeclause which is a qualification of the - * form ((= expr expr) (= expr expr) ...) and forms a new - * qualification like ((> expr expr) (> expr expr) ...) which - * is used by ExecMergeJoin() in order to determine if we should - * skip tuples. + * This takes the mergeclause which is a qualification of the + * form ((= expr expr) (= expr expr) ...) and forms a new + * qualification like ((> expr expr) (> expr expr) ...) which + * is used by ExecMergeJoin() in order to determine if we should + * skip tuples. * * old comments - * The 'qual' must be of the form: - * {(= outerkey1 innerkey1)(= outerkey2 innerkey2) ...} - * The "sortOp outerkey innerkey" is formed by substituting the "=" - * by "sortOp". + * The 'qual' must be of the form: + * {(= outerkey1 innerkey1)(= outerkey2 innerkey2) ...} + * The "sortOp outerkey innerkey" is formed by substituting the "=" + * by "sortOp". * ---------------------------------------------------------------- */ -static List * -MJFormOSortopI(List *qualList, Oid sortOp) +static List * +MJFormOSortopI(List * qualList, Oid sortOp) { - List *qualCopy; - List *qualcdr; - Expr *qual; - Oper *op; - - /* ---------------- - * qualList is a list: ((op .. ..) ...) - * first we make a copy of it. copyObject() makes a deep copy - * so let's use it instead of the old fashoned lispCopy()... - * ---------------- - */ - qualCopy = (List*) copyObject((Node*) qualList); - - foreach (qualcdr, qualCopy) { - /* ---------------- - * first get the current (op .. ..) list - * ---------------- - */ - qual = lfirst(qualcdr); - + List *qualCopy; + List *qualcdr; + Expr *qual; + Oper *op; + /* ---------------- - * now get at the op + * qualList is a list: ((op .. ..) ...) + * first we make a copy of it. copyObject() makes a deep copy + * so let's use it instead of the old fashoned lispCopy()... * ---------------- */ - op = (Oper*)qual->oper; - if (!IsA(op,Oper)) { - elog(DEBUG, "MJFormOSortopI: op not an Oper!"); - return NIL; + qualCopy = (List *) copyObject((Node *) qualList); + + foreach(qualcdr, qualCopy) + { + /* ---------------- + * first get the current (op .. ..) list + * ---------------- + */ + qual = lfirst(qualcdr); + + /* ---------------- + * now get at the op + * ---------------- + */ + op = (Oper *) qual->oper; + if (!IsA(op, Oper)) + { + elog(DEBUG, "MJFormOSortopI: op not an Oper!"); + return NIL; + } + + /* ---------------- + * change it's opid and since Op nodes now carry around a + * cached pointer to the associated op function, we have + * to make sure we invalidate this. Otherwise you get bizarre + * behavior when someone runs a mergejoin with _exec_repeat_ > 1 + * -cim 4/23/91 + * ---------------- + */ + op->opid = sortOp; + op->op_fcache = NULL; } - - /* ---------------- - * change it's opid and since Op nodes now carry around a - * cached pointer to the associated op function, we have - * to make sure we invalidate this. Otherwise you get bizarre - * behavior when someone runs a mergejoin with _exec_repeat_ > 1 - * -cim 4/23/91 - * ---------------- - */ - op->opid = sortOp; - op->op_fcache = NULL; - } - - return qualCopy; + + return qualCopy; } - + /* ---------------------------------------------------------------- - * MJFormISortopO + * MJFormISortopO * - * This does the same thing as MJFormOSortopI() except that - * it also reverses the expressions in the qualifications. - * For example: ((= expr1 expr2)) produces ((> expr2 expr1)) + * This does the same thing as MJFormOSortopI() except that + * it also reverses the expressions in the qualifications. + * For example: ((= expr1 expr2)) produces ((> expr2 expr1)) * * old comments - * The 'qual' must be of the form: - * {(= outerkey1 innerkey1) (= outerkey2 innerkey2) ...} - * The 'sortOp innerkey1 outerkey" is formed by substituting the "=" - * by "sortOp" and reversing the positions of the keys. - * ---------------------------------------------------------------- + * The 'qual' must be of the form: + * {(= outerkey1 innerkey1) (= outerkey2 innerkey2) ...} + * The 'sortOp innerkey1 outerkey" is formed by substituting the "=" + * by "sortOp" and reversing the positions of the keys. + * ---------------------------------------------------------------- */ -static List * -MJFormISortopO(List *qualList, Oid sortOp) +static List * +MJFormISortopO(List * qualList, Oid sortOp) { - List *ISortopO; - List *qualcdr; - - /* ---------------- - * first generate OSortopI, a list of the form - * ((op outer inner) (op outer inner) ... ) - * ---------------- - */ - ISortopO = MJFormOSortopI(qualList, sortOp); - - /* ---------------- - * now swap the cadr and caddr of each qual to form ISortopO, - * ((op inner outer) (op inner outer) ... ) - * ---------------- - */ - foreach (qualcdr, ISortopO) { - Expr *qual; - List *inner; - List *outer; - qual = lfirst(qualcdr); - - inner = lfirst(qual->args); - outer = lfirst(lnext(qual->args)); - lfirst(qual->args) = outer; - lfirst(lnext(qual->args)) = inner; - } - - return ISortopO; + List *ISortopO; + List *qualcdr; + + /* ---------------- + * first generate OSortopI, a list of the form + * ((op outer inner) (op outer inner) ... ) + * ---------------- + */ + ISortopO = MJFormOSortopI(qualList, sortOp); + + /* ---------------- + * now swap the cadr and caddr of each qual to form ISortopO, + * ((op inner outer) (op inner outer) ... ) + * ---------------- + */ + foreach(qualcdr, ISortopO) + { + Expr *qual; + List *inner; + List *outer; + + qual = lfirst(qualcdr); + + inner = lfirst(qual->args); + outer = lfirst(lnext(qual->args)); + lfirst(qual->args) = outer; + lfirst(lnext(qual->args)) = inner; + } + + return ISortopO; } - + /* ---------------------------------------------------------------- - * MergeCompare - * - * Compare the keys according to 'compareQual' which is of the - * form: {(key1a > key2a)(key1b > key2b) ...}. + * MergeCompare * - * (actually, it could also be the form (key1a < key2a)..) - * - * This is different from calling ExecQual because ExecQual returns - * true only if ALL the comparisions clauses are satisfied. - * However, there is an order of significance among the keys with - * the first keys being most significant. Therefore, the clauses - * are evaluated in order and the 'compareQual' is satisfied - * if (key1i > key2i) is true and (key1j = key2j) for 0 < j < i. + * Compare the keys according to 'compareQual' which is of the + * form: {(key1a > key2a)(key1b > key2b) ...}. + * + * (actually, it could also be the form (key1a < key2a)..) + * + * This is different from calling ExecQual because ExecQual returns + * true only if ALL the comparisions clauses are satisfied. + * However, there is an order of significance among the keys with + * the first keys being most significant. Therefore, the clauses + * are evaluated in order and the 'compareQual' is satisfied + * if (key1i > key2i) is true and (key1j = key2j) for 0 < j < i. * ---------------------------------------------------------------- */ -static bool -MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext) +static bool +MergeCompare(List * eqQual, List * compareQual, ExprContext * econtext) { - List *clause; - List *eqclause; - Datum const_value; - bool isNull; - bool isDone; - - /* ---------------- - * if we have no compare qualification, return nil - * ---------------- - */ - if (compareQual == NIL) - return false; - - /* ---------------- - * for each pair of clauses, test them until - * our compare conditions are satisified - * ---------------- - */ - eqclause = eqQual; - foreach (clause, compareQual) { + List *clause; + List *eqclause; + Datum const_value; + bool isNull; + bool isDone; + /* ---------------- - * first test if our compare clause is satisified. - * if so then return true. ignore isDone, don't iterate in - * quals. + * if we have no compare qualification, return nil * ---------------- */ - const_value = (Datum) - ExecEvalExpr((Node*) lfirst(clause), econtext, &isNull, &isDone); - - if (DatumGetInt32(const_value) != 0) - return true; - + if (compareQual == NIL) + return false; + /* ---------------- - * ok, the compare clause failed so we test if the keys - * are equal... if key1 != key2, we return false. - * otherwise key1 = key2 so we move on to the next pair of keys. - * - * ignore isDone, don't iterate in quals. + * for each pair of clauses, test them until + * our compare conditions are satisified * ---------------- */ - const_value = ExecEvalExpr((Node*) lfirst(eqclause), - econtext, - &isNull, - &isDone); - - if (DatumGetInt32(const_value) == 0) - return false; - eqclause = lnext(eqclause); - } - - /* ---------------- - * if we get here then it means none of our key greater-than - * conditions were satisified so we return false. - * ---------------- - */ - return false; + eqclause = eqQual; + foreach(clause, compareQual) + { + /* ---------------- + * first test if our compare clause is satisified. + * if so then return true. ignore isDone, don't iterate in + * quals. + * ---------------- + */ + const_value = (Datum) + ExecEvalExpr((Node *) lfirst(clause), econtext, &isNull, &isDone); + + if (DatumGetInt32(const_value) != 0) + return true; + + /* ---------------- + * ok, the compare clause failed so we test if the keys + * are equal... if key1 != key2, we return false. + * otherwise key1 = key2 so we move on to the next pair of keys. + * + * ignore isDone, don't iterate in quals. + * ---------------- + */ + const_value = ExecEvalExpr((Node *) lfirst(eqclause), + econtext, + &isNull, + &isDone); + + if (DatumGetInt32(const_value) == 0) + return false; + eqclause = lnext(eqclause); + } + + /* ---------------- + * if we get here then it means none of our key greater-than + * conditions were satisified so we return false. + * ---------------- + */ + return false; } - + /* ---------------------------------------------------------------- - * ExecMergeTupleDump + * ExecMergeTupleDump * - * This function is called through the MJ_dump() macro - * when EXEC_MERGEJOINDEBUG is defined + * This function is called through the MJ_dump() macro + * when EXEC_MERGEJOINDEBUG is defined * ---------------------------------------------------------------- */ #ifdef EXEC_MERGEJOINDEBUG void -ExecMergeTupleDumpInner(ExprContext *econtext) +ExecMergeTupleDumpInner(ExprContext * econtext) { - TupleTableSlot *innerSlot; - - printf("==== inner tuple ====\n"); - innerSlot = econtext->ecxt_innertuple; - if (TupIsNull(innerSlot)) - printf("(nil)\n"); - else - debugtup(innerSlot->val, - innerSlot->ttc_tupleDescriptor); + TupleTableSlot *innerSlot; + + printf("==== inner tuple ====\n"); + innerSlot = econtext->ecxt_innertuple; + if (TupIsNull(innerSlot)) + printf("(nil)\n"); + else + debugtup(innerSlot->val, + innerSlot->ttc_tupleDescriptor); } void -ExecMergeTupleDumpOuter(ExprContext *econtext) +ExecMergeTupleDumpOuter(ExprContext * econtext) { - TupleTableSlot *outerSlot; - - printf("==== outer tuple ====\n"); - outerSlot = econtext->ecxt_outertuple; - if (TupIsNull(outerSlot)) - printf("(nil)\n"); - else - debugtup(outerSlot->val, - outerSlot->ttc_tupleDescriptor); + TupleTableSlot *outerSlot; + + printf("==== outer tuple ====\n"); + outerSlot = econtext->ecxt_outertuple; + if (TupIsNull(outerSlot)) + printf("(nil)\n"); + else + debugtup(outerSlot->val, + outerSlot->ttc_tupleDescriptor); } void -ExecMergeTupleDumpMarked(ExprContext *econtext, - MergeJoinState *mergestate) +ExecMergeTupleDumpMarked(ExprContext * econtext, + MergeJoinState * mergestate) { - TupleTableSlot *markedSlot; + TupleTableSlot *markedSlot; - printf("==== marked tuple ====\n"); - markedSlot = mergestate->mj_MarkedTupleSlot; + printf("==== marked tuple ====\n"); + markedSlot = mergestate->mj_MarkedTupleSlot; - if (TupIsNull(markedSlot)) - printf("(nil)\n"); - else - debugtup(markedSlot->val, - markedSlot->ttc_tupleDescriptor); + if (TupIsNull(markedSlot)) + printf("(nil)\n"); + else + debugtup(markedSlot->val, + markedSlot->ttc_tupleDescriptor); } void -ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate) +ExecMergeTupleDump(ExprContext * econtext, MergeJoinState * mergestate) { - printf("******** ExecMergeTupleDump ********\n"); - - ExecMergeTupleDumpInner(econtext); - ExecMergeTupleDumpOuter(econtext); - ExecMergeTupleDumpMarked(econtext, mergestate); - - printf("******** \n"); + printf("******** ExecMergeTupleDump ********\n"); + + ExecMergeTupleDumpInner(econtext); + ExecMergeTupleDumpOuter(econtext); + ExecMergeTupleDumpMarked(econtext, mergestate); + + printf("******** \n"); } + #endif - + static void -CleanUpSort(Plan *plan) { - - if (plan == NULL) - return; - - if (plan->type == T_Sort) { - Sort *sort = (Sort *)plan; - psort_end(sort); - } +CleanUpSort(Plan * plan) +{ + + if (plan == NULL) + return; + + if (plan->type == T_Sort) + { + Sort *sort = (Sort *) plan; + + psort_end(sort); + } } /* ---------------------------------------------------------------- - * ExecMergeJoin + * ExecMergeJoin * * old comments - * Details of the merge-join routines: - * - * (1) ">" and "<" operators - * - * Merge-join is done by joining the inner and outer tuples satisfying - * the join clauses of the form ((= outerKey innerKey) ...). - * The join clauses is provided by the query planner and may contain - * more than one (= outerKey innerKey) clauses (for composite key). - * - * However, the query executor needs to know whether an outer - * tuple is "greater/smaller" than an inner tuple so that it can - * "synchronize" the two relations. For e.g., consider the following - * relations: - * - * outer: (0 ^1 1 2 5 5 5 6 6 7) current tuple: 1 - * inner: (1 ^3 5 5 5 5 6) current tuple: 3 - * - * To continue the merge-join, the executor needs to scan both inner - * and outer relations till the matching tuples 5. It needs to know - * that currently inner tuple 3 is "greater" than outer tuple 1 and - * therefore it should scan the outer relation first to find a - * matching tuple and so on. - * - * Therefore, when initializing the merge-join node, the executor - * creates the "greater/smaller" clause by substituting the "=" - * operator in the join clauses with the sort operator used to - * sort the outer and inner relation forming (outerKey sortOp innerKey). - * The sort operator is "<" if the relations are in ascending order - * otherwise, it is ">" if the relations are in descending order. - * The opposite "smaller/greater" clause is formed by reversing the - * outer and inner keys forming (innerKey sortOp outerKey). - * - * (2) repositioning inner "cursor" - * - * Consider the above relations and suppose that the executor has - * just joined the first outer "5" with the last inner "5". The - * next step is of course to join the second outer "5" with all - * the inner "5's". This requires repositioning the inner "cursor" - * to point at the first inner "5". This is done by "marking" the - * first inner 5 and restore the "cursor" to it before joining - * with the second outer 5. The access method interface provides - * routines to mark and restore to a tuple. + * Details of the merge-join routines: + * + * (1) ">" and "<" operators + * + * Merge-join is done by joining the inner and outer tuples satisfying + * the join clauses of the form ((= outerKey innerKey) ...). + * The join clauses is provided by the query planner and may contain + * more than one (= outerKey innerKey) clauses (for composite key). + * + * However, the query executor needs to know whether an outer + * tuple is "greater/smaller" than an inner tuple so that it can + * "synchronize" the two relations. For e.g., consider the following + * relations: + * + * outer: (0 ^1 1 2 5 5 5 6 6 7) current tuple: 1 + * inner: (1 ^3 5 5 5 5 6) current tuple: 3 + * + * To continue the merge-join, the executor needs to scan both inner + * and outer relations till the matching tuples 5. It needs to know + * that currently inner tuple 3 is "greater" than outer tuple 1 and + * therefore it should scan the outer relation first to find a + * matching tuple and so on. + * + * Therefore, when initializing the merge-join node, the executor + * creates the "greater/smaller" clause by substituting the "=" + * operator in the join clauses with the sort operator used to + * sort the outer and inner relation forming (outerKey sortOp innerKey). + * The sort operator is "<" if the relations are in ascending order + * otherwise, it is ">" if the relations are in descending order. + * The opposite "smaller/greater" clause is formed by reversing the + * outer and inner keys forming (innerKey sortOp outerKey). + * + * (2) repositioning inner "cursor" + * + * Consider the above relations and suppose that the executor has + * just joined the first outer "5" with the last inner "5". The + * next step is of course to join the second outer "5" with all + * the inner "5's". This requires repositioning the inner "cursor" + * to point at the first inner "5". This is done by "marking" the + * first inner 5 and restore the "cursor" to it before joining + * with the second outer 5. The access method interface provides + * routines to mark and restore to a tuple. * ---------------------------------------------------------------- */ TupleTableSlot * -ExecMergeJoin(MergeJoin *node) +ExecMergeJoin(MergeJoin * node) { - EState *estate; - MergeJoinState *mergestate; - ScanDirection direction; - List *innerSkipQual; - List *outerSkipQual; - List *mergeclauses; - List *qual; - bool qualResult; - bool compareResult; - - Plan *innerPlan; - TupleTableSlot *innerTupleSlot; - - Plan *outerPlan; - TupleTableSlot *outerTupleSlot; - - TupleTableSlot *markedTupleSlot; - - ExprContext *econtext; - - /* ---------------- - * get information from node - * ---------------- - */ - mergestate = node->mergestate; - estate = node->join.state; - direction = estate->es_direction; - innerPlan = innerPlan((Plan *)node); - outerPlan = outerPlan((Plan *)node); - econtext = mergestate->jstate.cs_ExprContext; - mergeclauses = node->mergeclauses; - qual = node->join.qual; - - if (ScanDirectionIsForward(direction)) { - outerSkipQual = mergestate->mj_OSortopI; - innerSkipQual = mergestate->mj_ISortopO; - } else { - outerSkipQual = mergestate->mj_ISortopO; - innerSkipQual = mergestate->mj_OSortopI; - } - - /* ---------------- - * ok, everything is setup.. let's go to work - * ---------------- - */ - if (mergestate->jstate.cs_TupFromTlist) { - TupleTableSlot *result; - ProjectionInfo *projInfo; - bool isDone; - - projInfo = mergestate->jstate.cs_ProjInfo; - result = ExecProject(projInfo, &isDone); - if (!isDone) - return result; - } - for (;;) { + EState *estate; + MergeJoinState *mergestate; + ScanDirection direction; + List *innerSkipQual; + List *outerSkipQual; + List *mergeclauses; + List *qual; + bool qualResult; + bool compareResult; + + Plan *innerPlan; + TupleTableSlot *innerTupleSlot; + + Plan *outerPlan; + TupleTableSlot *outerTupleSlot; + + TupleTableSlot *markedTupleSlot; + + ExprContext *econtext; + /* ---------------- - * get the current state of the join and do things accordingly. - * Note: The join states are highlighted with 32-* comments for - * improved readability. + * get information from node * ---------------- */ - MJ_dump(econtext, mergestate); - - switch (mergestate->mj_JoinState) { - /* ******************************** - * EXEC_MJ_INITIALIZE means that this is the first time - * ExecMergeJoin() has been called and so we have to - * initialize the inner, outer and marked tuples as well - * as various stuff in the expression context. - * ******************************** - */ - case EXEC_MJ_INITIALIZE: - MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n"); - /* ---------------- - * Note: at this point, if either of our inner or outer - * tuples are nil, then the join ends immediately because - * we know one of the subplans is empty. - * ---------------- - */ - innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node); - if (TupIsNull(innerTupleSlot)) { - MJ_printf("ExecMergeJoin: **** inner tuple is nil ****\n"); - return NULL; - } - - outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node); - if (TupIsNull(outerTupleSlot)) { - MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n"); - return NULL; - } - - /* ---------------- - * store the inner and outer tuple in the merge state - * ---------------- - */ - econtext->ecxt_innertuple = innerTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - - /* ---------------- - * set the marked tuple to nil - * and initialize its tuple descriptor atttributes. - * -jeff 10 july 1991 - * ---------------- - */ - ExecClearTuple(mergestate->mj_MarkedTupleSlot); - mergestate->mj_MarkedTupleSlot->ttc_tupleDescriptor = - innerTupleSlot->ttc_tupleDescriptor; -/* - mergestate->mj_MarkedTupleSlot->ttc_execTupDescriptor = - innerTupleSlot->ttc_execTupDescriptor; -*/ - /* ---------------- - * initialize merge join state to skip inner tuples. - * ---------------- - */ - mergestate->mj_JoinState = EXEC_MJ_SKIPINNER; - break; - - /* ******************************** - * EXEC_MJ_JOINMARK means we have just found a new - * outer tuple and a possible matching inner tuple. - * This is the case after the INITIALIZE, SKIPOUTER - * or SKIPINNER states. - * ******************************** - */ - case EXEC_MJ_JOINMARK: - MJ_printf("ExecMergeJoin: EXEC_MJ_JOINMARK\n"); - ExecMarkPos(innerPlan); - - innerTupleSlot = econtext->ecxt_innertuple; - MarkInnerTuple(innerTupleSlot, mergestate); - - mergestate->mj_JoinState = EXEC_MJ_JOINTEST; - break; - - /* ******************************** - * EXEC_MJ_JOINTEST means we have two tuples which - * might satisify the merge clause, so we test them. - * - * If they do satisify, then we join them and move - * on to the next inner tuple (EXEC_MJ_JOINTUPLES). - * - * If they do not satisify then advance to next outer tuple. - * ******************************** - */ - case EXEC_MJ_JOINTEST: - MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n"); - - qualResult = ExecQual((List*)mergeclauses, econtext); - MJ_DEBUG_QUAL(mergeclauses, qualResult); - - if (qualResult) - { - mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES; - } - else - { - mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER; - } - break; - - /* ******************************** - * EXEC_MJ_JOINTUPLES means we have two tuples which - * satisified the merge clause so we join them and then - * proceed to get the next inner tuple (EXEC_NEXT_INNER). - * ******************************** + mergestate = node->mergestate; + estate = node->join.state; + direction = estate->es_direction; + innerPlan = innerPlan((Plan *) node); + outerPlan = outerPlan((Plan *) node); + econtext = mergestate->jstate.cs_ExprContext; + mergeclauses = node->mergeclauses; + qual = node->join.qual; + + if (ScanDirectionIsForward(direction)) + { + outerSkipQual = mergestate->mj_OSortopI; + innerSkipQual = mergestate->mj_ISortopO; + } + else + { + outerSkipQual = mergestate->mj_ISortopO; + innerSkipQual = mergestate->mj_OSortopI; + } + + /* ---------------- + * ok, everything is setup.. let's go to work + * ---------------- */ - case EXEC_MJ_JOINTUPLES: - MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n"); - mergestate->mj_JoinState = EXEC_MJ_NEXTINNER; - - qualResult = ExecQual((List*)qual, econtext); - MJ_DEBUG_QUAL(qual, qualResult); - - if (qualResult) { - /* ---------------- - * qualification succeeded. now form the desired - * projection tuple and return the slot containing it. - * ---------------- - */ - ProjectionInfo *projInfo; + if (mergestate->jstate.cs_TupFromTlist) + { TupleTableSlot *result; - bool isDone; - - MJ_printf("ExecMergeJoin: **** returning tuple ****\n"); - + ProjectionInfo *projInfo; + bool isDone; + projInfo = mergestate->jstate.cs_ProjInfo; - result = ExecProject(projInfo, &isDone); - mergestate->jstate.cs_TupFromTlist = !isDone; - return result; - } - break; - - /* ******************************** - * EXEC_MJ_NEXTINNER means advance the inner scan - * to the next tuple. If the tuple is not nil, we then - * proceed to test it against the join qualification. - * ******************************** - */ - case EXEC_MJ_NEXTINNER: - MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n"); - - /* ---------------- - * now we get the next inner tuple, if any - * ---------------- - */ - innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node); - MJ_DEBUG_PROC_NODE(innerTupleSlot); - econtext->ecxt_innertuple = innerTupleSlot; - - if (TupIsNull(innerTupleSlot)) - { - mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER; - } - else - { - mergestate->mj_JoinState = EXEC_MJ_JOINTEST; - } - break; - - /* ******************************** - * EXEC_MJ_NEXTOUTER means - * - * outer inner - * outer tuple - 5 5 - marked tuple - * 5 5 - * 6 6 - inner tuple - * 7 7 - * - * we know we just bumped into - * the first inner tuple > current outer tuple - * so get a new outer tuple and then proceed to test - * it against the marked tuple (EXEC_MJ_TESTOUTER) - * ******************************** - */ - case EXEC_MJ_NEXTOUTER: - MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n"); - - outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node); - MJ_DEBUG_PROC_NODE(outerTupleSlot); - econtext->ecxt_outertuple = outerTupleSlot; - - /* ---------------- - * if the outer tuple is null then we know - * we are done with the join - * ---------------- - */ - if (TupIsNull(outerTupleSlot)) { - MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n"); - CleanUpSort(node->join.lefttree->lefttree); - CleanUpSort(node->join.righttree->lefttree); - return NULL; - } - - mergestate->mj_JoinState = EXEC_MJ_TESTOUTER; - break; - - /* ******************************** - * EXEC_MJ_TESTOUTER - * If the new outer tuple and the marked tuple satisify - * the merge clause then we know we have duplicates in - * the outer scan so we have to restore the inner scan - * to the marked tuple and proceed to join the new outer - * tuples with the inner tuples (EXEC_MJ_JOINTEST) - * - * This is the case when - * - * outer inner - * 4 5 - marked tuple - * outer tuple - 5 5 - * new outer tuple - 5 5 - * 6 8 - inner tuple - * 7 12 - * - * new outer tuple = marked tuple - * - * If the outer tuple fails the test, then we know we have - * to proceed to skip outer tuples until outer >= inner - * (EXEC_MJ_SKIPOUTER). - * - * This is the case when - * - * outer inner - * 5 5 - marked tuple - * outer tuple - 5 5 - * new outer tuple - 6 8 - inner tuple - * 7 12 - * - * new outer tuple > marked tuple - * - * ******************************** - */ - case EXEC_MJ_TESTOUTER: - MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n"); - - /* ---------------- - * here we compare the outer tuple with the marked inner tuple - * by using the marked tuple in place of the inner tuple. - * ---------------- - */ - innerTupleSlot = econtext->ecxt_innertuple; - markedTupleSlot = mergestate->mj_MarkedTupleSlot; - econtext->ecxt_innertuple = markedTupleSlot; - - qualResult = ExecQual((List*)mergeclauses, econtext); - MJ_DEBUG_QUAL(mergeclauses, qualResult); - - if (qualResult) { - /* ---------------- - * the merge clause matched so now we juggle the slots - * back the way they were and proceed to JOINTEST. - * ---------------- - */ - econtext->ecxt_innertuple = innerTupleSlot; - - RestoreInnerTuple(innerTupleSlot, markedTupleSlot); - - ExecRestrPos(innerPlan); - mergestate->mj_JoinState = EXEC_MJ_JOINTEST; - - } else { - /* ---------------- - * if the inner tuple was nil and the new outer - * tuple didn't match the marked outer tuple then - * we may have the case: - * - * outer inner - * 4 4 - marked tuple - * new outer - 5 4 - * 6 nil - inner tuple - * 7 - * - * which means that all subsequent outer tuples will be - * larger than our inner tuples. - * ---------------- - */ - if (TupIsNull(innerTupleSlot)) { - MJ_printf("ExecMergeJoin: **** wierd case 1 ****\n"); - return NULL; - } - - /* ---------------- - * restore the inner tuple and continue on to - * skip outer tuples. - * ---------------- - */ - econtext->ecxt_innertuple = innerTupleSlot; - mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER; - } - break; - - /* ******************************** - * EXEC_MJ_SKIPOUTER means skip over tuples in the outer plan - * until we find an outer tuple > current inner tuple. - * - * For example: - * - * outer inner - * 5 5 - * 5 5 - * outer tuple - 6 8 - inner tuple - * 7 12 - * 8 14 - * - * we have to advance the outer scan - * until we find the outer 8. - * - * ******************************** - */ - case EXEC_MJ_SKIPOUTER: - MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER\n"); - /* ---------------- - * before we advance, make sure the current tuples - * do not satisify the mergeclauses. If they do, then - * we update the marked tuple and go join them. - * ---------------- - */ - qualResult = ExecQual((List*)mergeclauses, econtext); - MJ_DEBUG_QUAL(mergeclauses, qualResult); - - if (qualResult) { - ExecMarkPos(innerPlan); - innerTupleSlot = econtext->ecxt_innertuple; - - MarkInnerTuple(innerTupleSlot, mergestate); - - mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES; - break; - } - - /* ---------------- - * ok, now test the skip qualification - * ---------------- - */ - compareResult = MergeCompare(mergeclauses, - outerSkipQual, - econtext); - - MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult); - - /* ---------------- - * compareResult is true as long as we should - * continue skipping tuples. - * ---------------- - */ - if (compareResult) { - - outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node); - MJ_DEBUG_PROC_NODE(outerTupleSlot); - econtext->ecxt_outertuple = outerTupleSlot; - - /* ---------------- - * if the outer tuple is null then we know - * we are done with the join - * ---------------- - */ - if (TupIsNull(outerTupleSlot)) { - MJ_printf("ExecMergeJoin: **** outerTuple is nil ****\n"); - return NULL; - } - /* ---------------- - * otherwise test the new tuple against the skip qual. - * (we remain in the EXEC_MJ_SKIPOUTER state) - * ---------------- - */ - break; - } - - /* ---------------- - * now check the inner skip qual to see if we - * should now skip inner tuples... if we fail the - * inner skip qual, then we know we have a new pair - * of matching tuples. - * ---------------- - */ - compareResult = MergeCompare(mergeclauses, - innerSkipQual, - econtext); - - MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult); - - if (compareResult) - { - mergestate->mj_JoinState = EXEC_MJ_SKIPINNER; - } - else - { - mergestate->mj_JoinState = EXEC_MJ_JOINMARK; - } - break; - - /* ******************************** - * EXEC_MJ_SKIPINNER means skip over tuples in the inner plan - * until we find an inner tuple > current outer tuple. - * - * For example: - * - * outer inner - * 5 5 - * 5 5 - * outer tuple - 12 8 - inner tuple - * 14 10 - * 17 12 - * - * we have to advance the inner scan - * until we find the inner 12. - * - * ******************************** - */ - case EXEC_MJ_SKIPINNER: - MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER\n"); - /* ---------------- - * before we advance, make sure the current tuples - * do not satisify the mergeclauses. If they do, then - * we update the marked tuple and go join them. - * ---------------- - */ - qualResult = ExecQual((List*)mergeclauses, econtext); - MJ_DEBUG_QUAL(mergeclauses, qualResult); - - if (qualResult) { - ExecMarkPos(innerPlan); - innerTupleSlot = econtext->ecxt_innertuple; - - MarkInnerTuple(innerTupleSlot, mergestate); - - mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES; - break; - } - - /* ---------------- - * ok, now test the skip qualification - * ---------------- - */ - compareResult = MergeCompare(mergeclauses, - innerSkipQual, - econtext); - - MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult); - - /* ---------------- - * compareResult is true as long as we should - * continue skipping tuples. - * ---------------- - */ - if (compareResult) { - /* ---------------- - * now try and get a new inner tuple - * ---------------- - */ - innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node); - MJ_DEBUG_PROC_NODE(innerTupleSlot); - econtext->ecxt_innertuple = innerTupleSlot; - + if (!isDone) + return result; + } + for (;;) + { /* ---------------- - * if the inner tuple is null then we know - * we have to restore the inner scan - * and advance to the next outer tuple + * get the current state of the join and do things accordingly. + * Note: The join states are highlighted with 32-* comments for + * improved readability. * ---------------- */ - if (TupIsNull(innerTupleSlot)) { - /* ---------------- - * this is an interesting case.. all our - * inner tuples are smaller then our outer - * tuples so we never found an inner tuple - * to mark. - * - * outer inner - * outer tuple - 5 4 - * 5 4 - * 6 nil - inner tuple - * 7 - * - * This means the join should end. - * ---------------- - */ - MJ_printf("ExecMergeJoin: **** wierd case 2 ****\n"); - return NULL; + MJ_dump(econtext, mergestate); + + switch (mergestate->mj_JoinState) + { + + /* + * ******************************** EXEC_MJ_INITIALIZE means + * that this is the first time ExecMergeJoin() has been called + * and so we have to initialize the inner, outer and marked + * tuples as well as various stuff in the expression context. ******************************** + * + */ + case EXEC_MJ_INITIALIZE: + MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n"); + /* ---------------- + * Note: at this point, if either of our inner or outer + * tuples are nil, then the join ends immediately because + * we know one of the subplans is empty. + * ---------------- + */ + innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node); + if (TupIsNull(innerTupleSlot)) + { + MJ_printf("ExecMergeJoin: **** inner tuple is nil ****\n"); + return NULL; + } + + outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node); + if (TupIsNull(outerTupleSlot)) + { + MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n"); + return NULL; + } + + /* ---------------- + * store the inner and outer tuple in the merge state + * ---------------- + */ + econtext->ecxt_innertuple = innerTupleSlot; + econtext->ecxt_outertuple = outerTupleSlot; + + /* ---------------- + * set the marked tuple to nil + * and initialize its tuple descriptor atttributes. + * -jeff 10 july 1991 + * ---------------- + */ + ExecClearTuple(mergestate->mj_MarkedTupleSlot); + mergestate->mj_MarkedTupleSlot->ttc_tupleDescriptor = + innerTupleSlot->ttc_tupleDescriptor; +/* + mergestate->mj_MarkedTupleSlot->ttc_execTupDescriptor = + innerTupleSlot->ttc_execTupDescriptor; +*/ + /* ---------------- + * initialize merge join state to skip inner tuples. + * ---------------- + */ + mergestate->mj_JoinState = EXEC_MJ_SKIPINNER; + break; + + /* + * ******************************** EXEC_MJ_JOINMARK means we + * have just found a new outer tuple and a possible matching + * inner tuple. This is the case after the INITIALIZE, + * SKIPOUTER or SKIPINNER states. ******************************** + * + */ + case EXEC_MJ_JOINMARK: + MJ_printf("ExecMergeJoin: EXEC_MJ_JOINMARK\n"); + ExecMarkPos(innerPlan); + + innerTupleSlot = econtext->ecxt_innertuple; + MarkInnerTuple(innerTupleSlot, mergestate); + + mergestate->mj_JoinState = EXEC_MJ_JOINTEST; + break; + + /* + * ******************************** EXEC_MJ_JOINTEST means we + * have two tuples which might satisify the merge clause, so + * we test them. + * + * If they do satisify, then we join them and move on to the next + * inner tuple (EXEC_MJ_JOINTUPLES). + * + * If they do not satisify then advance to next outer tuple. ******************************** + * + */ + case EXEC_MJ_JOINTEST: + MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n"); + + qualResult = ExecQual((List *) mergeclauses, econtext); + MJ_DEBUG_QUAL(mergeclauses, qualResult); + + if (qualResult) + { + mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES; + } + else + { + mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER; + } + break; + + /* + * ******************************** EXEC_MJ_JOINTUPLES means + * we have two tuples which satisified the merge clause so we + * join them and then proceed to get the next inner tuple + * (EXEC_NEXT_INNER). ******************************** + * + */ + case EXEC_MJ_JOINTUPLES: + MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n"); + mergestate->mj_JoinState = EXEC_MJ_NEXTINNER; + + qualResult = ExecQual((List *) qual, econtext); + MJ_DEBUG_QUAL(qual, qualResult); + + if (qualResult) + { + /* ---------------- + * qualification succeeded. now form the desired + * projection tuple and return the slot containing it. + * ---------------- + */ + ProjectionInfo *projInfo; + TupleTableSlot *result; + bool isDone; + + MJ_printf("ExecMergeJoin: **** returning tuple ****\n"); + + projInfo = mergestate->jstate.cs_ProjInfo; + + result = ExecProject(projInfo, &isDone); + mergestate->jstate.cs_TupFromTlist = !isDone; + return result; + } + break; + + /* + * ******************************** EXEC_MJ_NEXTINNER means + * advance the inner scan to the next tuple. If the tuple is + * not nil, we then proceed to test it against the join + * qualification. ******************************** + * + */ + case EXEC_MJ_NEXTINNER: + MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n"); + + /* ---------------- + * now we get the next inner tuple, if any + * ---------------- + */ + innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node); + MJ_DEBUG_PROC_NODE(innerTupleSlot); + econtext->ecxt_innertuple = innerTupleSlot; + + if (TupIsNull(innerTupleSlot)) + { + mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER; + } + else + { + mergestate->mj_JoinState = EXEC_MJ_JOINTEST; + } + break; + + /* + * ******************************** EXEC_MJ_NEXTOUTER means + * + * outer inner outer tuple - 5 5 - marked tuple 5 5 6 + * 6 - inner tuple 7 7 + * + * we know we just bumped into the first inner tuple > current + * outer tuple so get a new outer tuple and then proceed to + * test it against the marked tuple (EXEC_MJ_TESTOUTER) ******************************** + * + */ + case EXEC_MJ_NEXTOUTER: + MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n"); + + outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node); + MJ_DEBUG_PROC_NODE(outerTupleSlot); + econtext->ecxt_outertuple = outerTupleSlot; + + /* ---------------- + * if the outer tuple is null then we know + * we are done with the join + * ---------------- + */ + if (TupIsNull(outerTupleSlot)) + { + MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n"); + CleanUpSort(node->join.lefttree->lefttree); + CleanUpSort(node->join.righttree->lefttree); + return NULL; + } + + mergestate->mj_JoinState = EXEC_MJ_TESTOUTER; + break; + + /* + * ******************************** EXEC_MJ_TESTOUTER If the + * new outer tuple and the marked tuple satisify the merge + * clause then we know we have duplicates in the outer scan so + * we have to restore the inner scan to the marked tuple and + * proceed to join the new outer tuples with the inner tuples + * (EXEC_MJ_JOINTEST) + * + * This is the case when + * + * outer inner 4 5 - marked tuple outer tuple - 5 5 new + * outer tuple - 5 5 6 8 - inner tuple 7 12 + * + * new outer tuple = marked tuple + * + * If the outer tuple fails the test, then we know we have to + * proceed to skip outer tuples until outer >= inner + * (EXEC_MJ_SKIPOUTER). + * + * This is the case when + * + * outer inner 5 5 - marked tuple outer tuple - 5 5 new + * outer tuple - 6 8 - inner tuple 7 12 + * + * new outer tuple > marked tuple + * + ******************************** + * + */ + case EXEC_MJ_TESTOUTER: + MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n"); + + /* ---------------- + * here we compare the outer tuple with the marked inner tuple + * by using the marked tuple in place of the inner tuple. + * ---------------- + */ + innerTupleSlot = econtext->ecxt_innertuple; + markedTupleSlot = mergestate->mj_MarkedTupleSlot; + econtext->ecxt_innertuple = markedTupleSlot; + + qualResult = ExecQual((List *) mergeclauses, econtext); + MJ_DEBUG_QUAL(mergeclauses, qualResult); + + if (qualResult) + { + /* ---------------- + * the merge clause matched so now we juggle the slots + * back the way they were and proceed to JOINTEST. + * ---------------- + */ + econtext->ecxt_innertuple = innerTupleSlot; + + RestoreInnerTuple(innerTupleSlot, markedTupleSlot); + + ExecRestrPos(innerPlan); + mergestate->mj_JoinState = EXEC_MJ_JOINTEST; + + } + else + { + /* ---------------- + * if the inner tuple was nil and the new outer + * tuple didn't match the marked outer tuple then + * we may have the case: + * + * outer inner + * 4 4 - marked tuple + * new outer - 5 4 + * 6 nil - inner tuple + * 7 + * + * which means that all subsequent outer tuples will be + * larger than our inner tuples. + * ---------------- + */ + if (TupIsNull(innerTupleSlot)) + { + MJ_printf("ExecMergeJoin: **** wierd case 1 ****\n"); + return NULL; + } + + /* ---------------- + * restore the inner tuple and continue on to + * skip outer tuples. + * ---------------- + */ + econtext->ecxt_innertuple = innerTupleSlot; + mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER; + } + break; + + /* + * ******************************** EXEC_MJ_SKIPOUTER means + * skip over tuples in the outer plan until we find an outer + * tuple > current inner tuple. + * + * For example: + * + * outer inner 5 5 5 5 outer tuple - 6 8 - inner + * tuple 7 12 8 14 + * + * we have to advance the outer scan until we find the outer 8. + * + ******************************** + * + */ + case EXEC_MJ_SKIPOUTER: + MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER\n"); + /* ---------------- + * before we advance, make sure the current tuples + * do not satisify the mergeclauses. If they do, then + * we update the marked tuple and go join them. + * ---------------- + */ + qualResult = ExecQual((List *) mergeclauses, econtext); + MJ_DEBUG_QUAL(mergeclauses, qualResult); + + if (qualResult) + { + ExecMarkPos(innerPlan); + innerTupleSlot = econtext->ecxt_innertuple; + + MarkInnerTuple(innerTupleSlot, mergestate); + + mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES; + break; + } + + /* ---------------- + * ok, now test the skip qualification + * ---------------- + */ + compareResult = MergeCompare(mergeclauses, + outerSkipQual, + econtext); + + MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult); + + /* ---------------- + * compareResult is true as long as we should + * continue skipping tuples. + * ---------------- + */ + if (compareResult) + { + + outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node); + MJ_DEBUG_PROC_NODE(outerTupleSlot); + econtext->ecxt_outertuple = outerTupleSlot; + + /* ---------------- + * if the outer tuple is null then we know + * we are done with the join + * ---------------- + */ + if (TupIsNull(outerTupleSlot)) + { + MJ_printf("ExecMergeJoin: **** outerTuple is nil ****\n"); + return NULL; + } + /* ---------------- + * otherwise test the new tuple against the skip qual. + * (we remain in the EXEC_MJ_SKIPOUTER state) + * ---------------- + */ + break; + } + + /* ---------------- + * now check the inner skip qual to see if we + * should now skip inner tuples... if we fail the + * inner skip qual, then we know we have a new pair + * of matching tuples. + * ---------------- + */ + compareResult = MergeCompare(mergeclauses, + innerSkipQual, + econtext); + + MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult); + + if (compareResult) + { + mergestate->mj_JoinState = EXEC_MJ_SKIPINNER; + } + else + { + mergestate->mj_JoinState = EXEC_MJ_JOINMARK; + } + break; + + /* + * ******************************** EXEC_MJ_SKIPINNER means + * skip over tuples in the inner plan until we find an inner + * tuple > current outer tuple. + * + * For example: + * + * outer inner 5 5 5 5 outer tuple - 12 8 - inner + * tuple 14 10 17 12 + * + * we have to advance the inner scan until we find the inner 12. + * + ******************************** + * + */ + case EXEC_MJ_SKIPINNER: + MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER\n"); + /* ---------------- + * before we advance, make sure the current tuples + * do not satisify the mergeclauses. If they do, then + * we update the marked tuple and go join them. + * ---------------- + */ + qualResult = ExecQual((List *) mergeclauses, econtext); + MJ_DEBUG_QUAL(mergeclauses, qualResult); + + if (qualResult) + { + ExecMarkPos(innerPlan); + innerTupleSlot = econtext->ecxt_innertuple; + + MarkInnerTuple(innerTupleSlot, mergestate); + + mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES; + break; + } + + /* ---------------- + * ok, now test the skip qualification + * ---------------- + */ + compareResult = MergeCompare(mergeclauses, + innerSkipQual, + econtext); + + MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult); + + /* ---------------- + * compareResult is true as long as we should + * continue skipping tuples. + * ---------------- + */ + if (compareResult) + { + /* ---------------- + * now try and get a new inner tuple + * ---------------- + */ + innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node); + MJ_DEBUG_PROC_NODE(innerTupleSlot); + econtext->ecxt_innertuple = innerTupleSlot; + + /* ---------------- + * if the inner tuple is null then we know + * we have to restore the inner scan + * and advance to the next outer tuple + * ---------------- + */ + if (TupIsNull(innerTupleSlot)) + { + /* ---------------- + * this is an interesting case.. all our + * inner tuples are smaller then our outer + * tuples so we never found an inner tuple + * to mark. + * + * outer inner + * outer tuple - 5 4 + * 5 4 + * 6 nil - inner tuple + * 7 + * + * This means the join should end. + * ---------------- + */ + MJ_printf("ExecMergeJoin: **** wierd case 2 ****\n"); + return NULL; + } + + /* ---------------- + * otherwise test the new tuple against the skip qual. + * (we remain in the EXEC_MJ_SKIPINNER state) + * ---------------- + */ + break; + } + + /* ---------------- + * compare finally failed and we have stopped skipping + * inner tuples so now check the outer skip qual + * to see if we should now skip outer tuples... + * ---------------- + */ + compareResult = MergeCompare(mergeclauses, + outerSkipQual, + econtext); + + MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult); + + if (compareResult) + { + mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER; + } + else + { + mergestate->mj_JoinState = EXEC_MJ_JOINMARK; + } + + break; + + /* + * ******************************** if we get here it means + * our code is fucked up and so we just end the join + * prematurely. ******************************** + * + */ + default: + elog(NOTICE, "ExecMergeJoin: invalid join state. aborting"); + return NULL; } - - /* ---------------- - * otherwise test the new tuple against the skip qual. - * (we remain in the EXEC_MJ_SKIPINNER state) - * ---------------- - */ - break; - } - - /* ---------------- - * compare finally failed and we have stopped skipping - * inner tuples so now check the outer skip qual - * to see if we should now skip outer tuples... - * ---------------- - */ - compareResult = MergeCompare(mergeclauses, - outerSkipQual, - econtext); - - MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult); - - if (compareResult) - { - mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER; - } - else - { - mergestate->mj_JoinState = EXEC_MJ_JOINMARK; - } - - break; - - /* ******************************** - * if we get here it means our code is fucked up and - * so we just end the join prematurely. - * ******************************** - */ - default: - elog(NOTICE, "ExecMergeJoin: invalid join state. aborting"); - return NULL; } - } } - + /* ---------------------------------------------------------------- - * ExecInitMergeJoin + * ExecInitMergeJoin * * old comments - * Creates the run-time state information for the node and - * sets the relation id to contain relevant decriptors. + * Creates the run-time state information for the node and + * sets the relation id to contain relevant decriptors. * ---------------------------------------------------------------- */ bool -ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent) +ExecInitMergeJoin(MergeJoin * node, EState * estate, Plan * parent) { - MergeJoinState *mergestate; - List *joinclauses; - RegProcedure rightsortop; - RegProcedure leftsortop; - RegProcedure sortop; - - List *OSortopI; - List *ISortopO; - - MJ1_printf("ExecInitMergeJoin: %s\n", - "initializing node"); - - /* ---------------- - * assign the node's execution state and - * get the range table and direction from it - * ---------------- - */ - node->join.state = estate; - - /* ---------------- - * create new merge state for node - * ---------------- - */ - mergestate = makeNode(MergeJoinState); - mergestate->mj_OSortopI = NIL; - mergestate->mj_ISortopO = NIL; - mergestate->mj_JoinState = 0; - mergestate->mj_MarkedTupleSlot = NULL; - node->mergestate = mergestate; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks and - * + create expression context for node - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &mergestate->jstate, parent); - ExecAssignExprContext(estate, &mergestate->jstate); + MergeJoinState *mergestate; + List *joinclauses; + RegProcedure rightsortop; + RegProcedure leftsortop; + RegProcedure sortop; + + List *OSortopI; + List *ISortopO; + + MJ1_printf("ExecInitMergeJoin: %s\n", + "initializing node"); + + /* ---------------- + * assign the node's execution state and + * get the range table and direction from it + * ---------------- + */ + node->join.state = estate; + + /* ---------------- + * create new merge state for node + * ---------------- + */ + mergestate = makeNode(MergeJoinState); + mergestate->mj_OSortopI = NIL; + mergestate->mj_ISortopO = NIL; + mergestate->mj_JoinState = 0; + mergestate->mj_MarkedTupleSlot = NULL; + node->mergestate = mergestate; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks and + * + create expression context for node + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &mergestate->jstate, parent); + ExecAssignExprContext(estate, &mergestate->jstate); #define MERGEJOIN_NSLOTS 2 - /* ---------------- - * tuple table initialization - * ---------------- - */ - ExecInitResultTupleSlot(estate, &mergestate->jstate); - ExecInitMarkedTupleSlot(estate, mergestate); - - /* ---------------- - * get merge sort operators. - * - * XXX for now we assume all quals in the joinclauses were - * sorted with the same operator in both the inner and - * outer relations. -cim 11/2/89 - * ---------------- - */ - joinclauses = node->mergeclauses; - - rightsortop = get_opcode(node->mergerightorder[0]); - leftsortop = get_opcode(node->mergeleftorder[0]); - - if (leftsortop != rightsortop) - elog(NOTICE, "ExecInitMergeJoin: %s", - "left and right sortop's are unequal!"); - - sortop = rightsortop; - - /* ---------------- - * form merge skip qualifications - * - * XXX MJform routines need to be extended - * to take a list of sortops.. -cim 11/2/89 - * ---------------- - */ - OSortopI = MJFormOSortopI(joinclauses, sortop); - ISortopO = MJFormISortopO(joinclauses, sortop); - mergestate->mj_OSortopI = OSortopI; - mergestate->mj_ISortopO = ISortopO; - - MJ_printf("\nExecInitMergeJoin: OSortopI is "); - MJ_nodeDisplay(OSortopI); - MJ_printf("\nExecInitMergeJoin: ISortopO is "); - MJ_nodeDisplay(ISortopO); - MJ_printf("\n"); - - /* ---------------- - * initialize join state - * ---------------- - */ - mergestate->mj_JoinState = EXEC_MJ_INITIALIZE; - - /* ---------------- - * initialize subplans - * ---------------- - */ - ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node); - ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node); - - /* ---------------- - * initialize tuple type and projection info - * ---------------- - */ - ExecAssignResultTypeFromTL((Plan *) node, &mergestate->jstate); - ExecAssignProjectionInfo((Plan *) node, &mergestate->jstate); - - mergestate->jstate.cs_TupFromTlist = false; - /* ---------------- - * initialization successful - * ---------------- - */ - MJ1_printf("ExecInitMergeJoin: %s\n", - "node initialized"); - - return TRUE; + /* ---------------- + * tuple table initialization + * ---------------- + */ + ExecInitResultTupleSlot(estate, &mergestate->jstate); + ExecInitMarkedTupleSlot(estate, mergestate); + + /* ---------------- + * get merge sort operators. + * + * XXX for now we assume all quals in the joinclauses were + * sorted with the same operator in both the inner and + * outer relations. -cim 11/2/89 + * ---------------- + */ + joinclauses = node->mergeclauses; + + rightsortop = get_opcode(node->mergerightorder[0]); + leftsortop = get_opcode(node->mergeleftorder[0]); + + if (leftsortop != rightsortop) + elog(NOTICE, "ExecInitMergeJoin: %s", + "left and right sortop's are unequal!"); + + sortop = rightsortop; + + /* ---------------- + * form merge skip qualifications + * + * XXX MJform routines need to be extended + * to take a list of sortops.. -cim 11/2/89 + * ---------------- + */ + OSortopI = MJFormOSortopI(joinclauses, sortop); + ISortopO = MJFormISortopO(joinclauses, sortop); + mergestate->mj_OSortopI = OSortopI; + mergestate->mj_ISortopO = ISortopO; + + MJ_printf("\nExecInitMergeJoin: OSortopI is "); + MJ_nodeDisplay(OSortopI); + MJ_printf("\nExecInitMergeJoin: ISortopO is "); + MJ_nodeDisplay(ISortopO); + MJ_printf("\n"); + + /* ---------------- + * initialize join state + * ---------------- + */ + mergestate->mj_JoinState = EXEC_MJ_INITIALIZE; + + /* ---------------- + * initialize subplans + * ---------------- + */ + ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node); + ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node); + + /* ---------------- + * initialize tuple type and projection info + * ---------------- + */ + ExecAssignResultTypeFromTL((Plan *) node, &mergestate->jstate); + ExecAssignProjectionInfo((Plan *) node, &mergestate->jstate); + + mergestate->jstate.cs_TupFromTlist = false; + /* ---------------- + * initialization successful + * ---------------- + */ + MJ1_printf("ExecInitMergeJoin: %s\n", + "node initialized"); + + return TRUE; } - + int -ExecCountSlotsMergeJoin(MergeJoin *node) +ExecCountSlotsMergeJoin(MergeJoin * node) { - return ExecCountSlotsNode(outerPlan((Plan *)node)) + - ExecCountSlotsNode(innerPlan((Plan *)node)) + - MERGEJOIN_NSLOTS; + return ExecCountSlotsNode(outerPlan((Plan *) node)) + + ExecCountSlotsNode(innerPlan((Plan *) node)) + + MERGEJOIN_NSLOTS; } - + /* ---------------------------------------------------------------- - * ExecEndMergeJoin + * ExecEndMergeJoin * * old comments - * frees storage allocated through C routines. + * frees storage allocated through C routines. * ---------------------------------------------------------------- */ void -ExecEndMergeJoin(MergeJoin *node) +ExecEndMergeJoin(MergeJoin * node) { - MergeJoinState *mergestate; - - MJ1_printf("ExecEndMergeJoin: %s\n", - "ending node processing"); - - /* ---------------- - * get state information from the node - * ---------------- - */ - mergestate = node->mergestate; - - /* ---------------- - * Free the projection info and the scan attribute info - * - * Note: we don't ExecFreeResultType(mergestate) - * because the rule manager depends on the tupType - * returned by ExecMain(). So for now, this - * is freed at end-transaction time. -cim 6/2/91 - * ---------------- - */ - ExecFreeProjectionInfo(&mergestate->jstate); - - /* ---------------- - * shut down the subplans - * ---------------- - */ - ExecEndNode((Plan*) innerPlan((Plan *) node), (Plan*)node); - ExecEndNode((Plan*) outerPlan((Plan *) node), (Plan*)node); - - /* ---------------- - * clean out the tuple table so that we don't try and - * pfree the marked tuples.. see HACK ALERT at the top of - * this file. - * ---------------- - */ - ExecClearTuple(mergestate->jstate.cs_ResultTupleSlot); - ExecClearTuple(mergestate->mj_MarkedTupleSlot); - - MJ1_printf("ExecEndMergeJoin: %s\n", - "node processing ended"); + MergeJoinState *mergestate; + + MJ1_printf("ExecEndMergeJoin: %s\n", + "ending node processing"); + + /* ---------------- + * get state information from the node + * ---------------- + */ + mergestate = node->mergestate; + + /* ---------------- + * Free the projection info and the scan attribute info + * + * Note: we don't ExecFreeResultType(mergestate) + * because the rule manager depends on the tupType + * returned by ExecMain(). So for now, this + * is freed at end-transaction time. -cim 6/2/91 + * ---------------- + */ + ExecFreeProjectionInfo(&mergestate->jstate); + + /* ---------------- + * shut down the subplans + * ---------------- + */ + ExecEndNode((Plan *) innerPlan((Plan *) node), (Plan *) node); + ExecEndNode((Plan *) outerPlan((Plan *) node), (Plan *) node); + + /* ---------------- + * clean out the tuple table so that we don't try and + * pfree the marked tuples.. see HACK ALERT at the top of + * this file. + * ---------------- + */ + ExecClearTuple(mergestate->jstate.cs_ResultTupleSlot); + ExecClearTuple(mergestate->mj_MarkedTupleSlot); + + MJ1_printf("ExecEndMergeJoin: %s\n", + "node processing ended"); } - diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index d83d306bba9..e7cba2e756e 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -1,21 +1,21 @@ /*------------------------------------------------------------------------- * * nodeNestloop.c-- - * routines to support nest-loop joins + * routines to support nest-loop joins * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.3 1996/11/08 05:56:15 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.4 1997/09/07 04:41:41 momjian Exp $ * *------------------------------------------------------------------------- */ /* - * INTERFACE ROUTINES - * ExecNestLoop - process a nestloop join of two plans - * ExecInitNestLoop - initialize the join - * ExecEndNestLoop - shut down the join + * INTERFACE ROUTINES + * ExecNestLoop - process a nestloop join of two plans + * ExecInitNestLoop - initialize the join + * ExecEndNestLoop - shut down the join */ #include "postgres.h" @@ -25,349 +25,363 @@ #include "executor/nodeIndexscan.h" /* ---------------------------------------------------------------- - * ExecNestLoop(node) + * ExecNestLoop(node) * * old comments - * Returns the tuple joined from inner and outer tuples which - * satisfies the qualification clause. + * Returns the tuple joined from inner and outer tuples which + * satisfies the qualification clause. * - * It scans the inner relation to join with current outer tuple. + * It scans the inner relation to join with current outer tuple. * - * If none is found, next tuple form the outer relation is retrieved - * and the inner relation is scanned from the beginning again to join - * with the outer tuple. + * If none is found, next tuple form the outer relation is retrieved + * and the inner relation is scanned from the beginning again to join + * with the outer tuple. * - * Nil is returned if all the remaining outer tuples are tried and - * all fail to join with the inner tuples. + * Nil is returned if all the remaining outer tuples are tried and + * all fail to join with the inner tuples. * - * Nil is also returned if there is no tuple from inner realtion. - * - * Conditions: - * -- outerTuple contains current tuple from outer relation and - * the right son(inner realtion) maintains "cursor" at the tuple - * returned previously. - * This is achieved by maintaining a scan position on the outer - * relation. - * - * Initial States: - * -- the outer child and the inner child - * are prepared to return the first tuple. + * Nil is also returned if there is no tuple from inner realtion. + * + * Conditions: + * -- outerTuple contains current tuple from outer relation and + * the right son(inner realtion) maintains "cursor" at the tuple + * returned previously. + * This is achieved by maintaining a scan position on the outer + * relation. + * + * Initial States: + * -- the outer child and the inner child + * are prepared to return the first tuple. * ---------------------------------------------------------------- */ TupleTableSlot * -ExecNestLoop(NestLoop *node, Plan* parent) +ExecNestLoop(NestLoop * node, Plan * parent) { - NestLoopState *nlstate; - Plan *innerPlan; - Plan *outerPlan; - bool needNewOuterTuple; - - TupleTableSlot *outerTupleSlot; - TupleTableSlot *innerTupleSlot; - - List *qual; - bool qualResult; - ExprContext *econtext; - - /* ---------------- - * get information from the node - * ---------------- - */ - ENL1_printf("getting info from node"); - - nlstate = node->nlstate; - qual = node->join.qual; - outerPlan = outerPlan(&node->join); - innerPlan = innerPlan(&node->join); - - /* ---------------- - * initialize expression context - * ---------------- - */ - econtext = nlstate->jstate.cs_ExprContext; - - /* ---------------- * get the current outer tuple - * ---------------- - */ - outerTupleSlot = nlstate->jstate.cs_OuterTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - - /* ---------------- - * Ok, everything is setup for the join so now loop until - * we return a qualifying join tuple.. - * ---------------- - */ - - if (nlstate->jstate.cs_TupFromTlist) { - TupleTableSlot *result; - bool isDone; - - result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone); - if (!isDone) - return result; - } - - ENL1_printf("entering main loop"); - for(;;) { + NestLoopState *nlstate; + Plan *innerPlan; + Plan *outerPlan; + bool needNewOuterTuple; + + TupleTableSlot *outerTupleSlot; + TupleTableSlot *innerTupleSlot; + + List *qual; + bool qualResult; + ExprContext *econtext; + /* ---------------- - * The essential idea now is to get the next inner tuple - * and join it with the current outer tuple. + * get information from the node * ---------------- */ - needNewOuterTuple = false; - + ENL1_printf("getting info from node"); + + nlstate = node->nlstate; + qual = node->join.qual; + outerPlan = outerPlan(&node->join); + innerPlan = innerPlan(&node->join); + /* ---------------- - * If outer tuple is not null then that means - * we are in the middle of a scan and we should - * restore our previously saved scan position. + * initialize expression context * ---------------- */ - if (! TupIsNull(outerTupleSlot)) { - ENL1_printf("have outer tuple, restoring outer plan"); - ExecRestrPos(outerPlan); - } else { - ENL1_printf("outer tuple is nil, need new outer tuple"); - needNewOuterTuple = true; - } - - /* ---------------- - * if we have an outerTuple, try to get the next inner tuple. + econtext = nlstate->jstate.cs_ExprContext; + + /* ---------------- * get the current outer tuple * ---------------- */ - if (!needNewOuterTuple) { - ENL1_printf("getting new inner tuple"); - - innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node); - econtext->ecxt_innertuple = innerTupleSlot; - - if (TupIsNull(innerTupleSlot)) { - ENL1_printf("no inner tuple, need new outer tuple"); - needNewOuterTuple = true; - } - } - + outerTupleSlot = nlstate->jstate.cs_OuterTupleSlot; + econtext->ecxt_outertuple = outerTupleSlot; + /* ---------------- - * loop until we have a new outer tuple and a new - * inner tuple. + * Ok, everything is setup for the join so now loop until + * we return a qualifying join tuple.. * ---------------- */ - while (needNewOuterTuple) { - /* ---------------- - * now try to get the next outer tuple - * ---------------- - */ - ENL1_printf("getting new outer tuple"); - outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node); - econtext->ecxt_outertuple = outerTupleSlot; - - /* ---------------- - * if there are no more outer tuples, then the join - * is complete.. - * ---------------- - */ - if (TupIsNull(outerTupleSlot)) { - ENL1_printf("no outer tuple, ending join"); - return NULL; - } - - /* ---------------- - * we have a new outer tuple so we mark our position - * in the outer scan and save the outer tuple in the - * NestLoop state - * ---------------- - */ - ENL1_printf("saving new outer tuple information"); - ExecMarkPos(outerPlan); - nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot; - - /* ---------------- - * now rescan the inner plan and get a new inner tuple - * ---------------- - */ - - ENL1_printf("rescanning inner plan"); - /* - * The scan key of the inner plan might depend on the current - * outer tuple (e.g. in index scans), that's why we pass our - * expr context. - */ - ExecReScan(innerPlan, econtext, parent); - - ENL1_printf("getting new inner tuple"); - - innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node); - econtext->ecxt_innertuple = innerTupleSlot; - - if (TupIsNull(innerTupleSlot)) { - ENL1_printf("couldn't get inner tuple - need new outer tuple"); - } else { - ENL1_printf("got inner and outer tuples"); + + if (nlstate->jstate.cs_TupFromTlist) + { + TupleTableSlot *result; + bool isDone; + + result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone); + if (!isDone) + return result; + } + + ENL1_printf("entering main loop"); + for (;;) + { + /* ---------------- + * The essential idea now is to get the next inner tuple + * and join it with the current outer tuple. + * ---------------- + */ needNewOuterTuple = false; - } - } /* while (needNewOuterTuple) */ - + + /* ---------------- + * If outer tuple is not null then that means + * we are in the middle of a scan and we should + * restore our previously saved scan position. + * ---------------- + */ + if (!TupIsNull(outerTupleSlot)) + { + ENL1_printf("have outer tuple, restoring outer plan"); + ExecRestrPos(outerPlan); + } + else + { + ENL1_printf("outer tuple is nil, need new outer tuple"); + needNewOuterTuple = true; + } + + /* ---------------- + * if we have an outerTuple, try to get the next inner tuple. + * ---------------- + */ + if (!needNewOuterTuple) + { + ENL1_printf("getting new inner tuple"); + + innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node); + econtext->ecxt_innertuple = innerTupleSlot; + + if (TupIsNull(innerTupleSlot)) + { + ENL1_printf("no inner tuple, need new outer tuple"); + needNewOuterTuple = true; + } + } + + /* ---------------- + * loop until we have a new outer tuple and a new + * inner tuple. + * ---------------- + */ + while (needNewOuterTuple) + { + /* ---------------- + * now try to get the next outer tuple + * ---------------- + */ + ENL1_printf("getting new outer tuple"); + outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node); + econtext->ecxt_outertuple = outerTupleSlot; + + /* ---------------- + * if there are no more outer tuples, then the join + * is complete.. + * ---------------- + */ + if (TupIsNull(outerTupleSlot)) + { + ENL1_printf("no outer tuple, ending join"); + return NULL; + } + + /* ---------------- + * we have a new outer tuple so we mark our position + * in the outer scan and save the outer tuple in the + * NestLoop state + * ---------------- + */ + ENL1_printf("saving new outer tuple information"); + ExecMarkPos(outerPlan); + nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot; + + /* ---------------- + * now rescan the inner plan and get a new inner tuple + * ---------------- + */ + + ENL1_printf("rescanning inner plan"); + + /* + * The scan key of the inner plan might depend on the current + * outer tuple (e.g. in index scans), that's why we pass our + * expr context. + */ + ExecReScan(innerPlan, econtext, parent); + + ENL1_printf("getting new inner tuple"); + + innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node); + econtext->ecxt_innertuple = innerTupleSlot; + + if (TupIsNull(innerTupleSlot)) + { + ENL1_printf("couldn't get inner tuple - need new outer tuple"); + } + else + { + ENL1_printf("got inner and outer tuples"); + needNewOuterTuple = false; + } + } /* while (needNewOuterTuple) */ + + /* ---------------- + * at this point we have a new pair of inner and outer + * tuples so we test the inner and outer tuples to see + * if they satisify the node's qualification. + * ---------------- + */ + ENL1_printf("testing qualification"); + qualResult = ExecQual((List *) qual, econtext); + + if (qualResult) + { + /* ---------------- + * qualification was satisified so we project and + * return the slot containing the result tuple + * using ExecProject(). + * ---------------- + */ + ProjectionInfo *projInfo; + TupleTableSlot *result; + bool isDone; + + ENL1_printf("qualification succeeded, projecting tuple"); + + projInfo = nlstate->jstate.cs_ProjInfo; + result = ExecProject(projInfo, &isDone); + nlstate->jstate.cs_TupFromTlist = !isDone; + return result; + } + + /* ---------------- + * qualification failed so we have to try again.. + * ---------------- + */ + ENL1_printf("qualification failed, looping"); + } +} + +/* ---------------------------------------------------------------- + * ExecInitNestLoop + * + * Creates the run-time state information for the nestloop node + * produced by the planner and initailizes inner and outer relations + * (child nodes). + * ---------------------------------------------------------------- + */ +bool +ExecInitNestLoop(NestLoop * node, EState * estate, Plan * parent) +{ + NestLoopState *nlstate; + + NL1_printf("ExecInitNestLoop: %s\n", + "initializing node"); + /* ---------------- - * at this point we have a new pair of inner and outer - * tuples so we test the inner and outer tuples to see - * if they satisify the node's qualification. + * assign execution state to node * ---------------- */ - ENL1_printf("testing qualification"); - qualResult = ExecQual((List*)qual, econtext); - - if (qualResult) { - /* ---------------- - * qualification was satisified so we project and - * return the slot containing the result tuple - * using ExecProject(). - * ---------------- - */ - ProjectionInfo *projInfo; - TupleTableSlot *result; - bool isDone; - - ENL1_printf("qualification succeeded, projecting tuple"); - - projInfo = nlstate->jstate.cs_ProjInfo; - result = ExecProject(projInfo, &isDone); - nlstate->jstate.cs_TupFromTlist = !isDone; - return result; - } - + node->join.state = estate; + /* ---------------- - * qualification failed so we have to try again.. + * create new nest loop state * ---------------- */ - ENL1_printf("qualification failed, looping"); - } -} + nlstate = makeNode(NestLoopState); + nlstate->nl_PortalFlag = false; + node->nlstate = nlstate; -/* ---------------------------------------------------------------- - * ExecInitNestLoop - * - * Creates the run-time state information for the nestloop node - * produced by the planner and initailizes inner and outer relations - * (child nodes). - * ---------------------------------------------------------------- - */ -bool -ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent) -{ - NestLoopState *nlstate; - - NL1_printf("ExecInitNestLoop: %s\n", - "initializing node"); - - /* ---------------- - * assign execution state to node - * ---------------- - */ - node->join.state = estate; - - /* ---------------- - * create new nest loop state - * ---------------- - */ - nlstate = makeNode(NestLoopState); - nlstate->nl_PortalFlag = false; - node->nlstate = nlstate; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks and - * + create expression context for node - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &nlstate->jstate, parent); - ExecAssignExprContext(estate, &nlstate->jstate); + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks and + * + create expression context for node + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &nlstate->jstate, parent); + ExecAssignExprContext(estate, &nlstate->jstate); #define NESTLOOP_NSLOTS 1 - /* ---------------- - * tuple table initialization - * ---------------- - */ - ExecInitResultTupleSlot(estate, &nlstate->jstate); - - /* ---------------- - * now initialize children - * ---------------- - */ - ExecInitNode(outerPlan((Plan*)node), estate, (Plan*)node); - ExecInitNode(innerPlan((Plan*)node), estate, (Plan*)node); - - /* ---------------- - * initialize tuple type and projection info - * ---------------- - */ - ExecAssignResultTypeFromTL((Plan *) node, &nlstate->jstate); - ExecAssignProjectionInfo((Plan *) node, &nlstate->jstate); - - /* ---------------- - * finally, wipe the current outer tuple clean. - * ---------------- - */ - nlstate->jstate.cs_OuterTupleSlot = NULL; - nlstate->jstate.cs_TupFromTlist = false; - - NL1_printf("ExecInitNestLoop: %s\n", - "node initialized"); - return TRUE; + /* ---------------- + * tuple table initialization + * ---------------- + */ + ExecInitResultTupleSlot(estate, &nlstate->jstate); + + /* ---------------- + * now initialize children + * ---------------- + */ + ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node); + ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node); + + /* ---------------- + * initialize tuple type and projection info + * ---------------- + */ + ExecAssignResultTypeFromTL((Plan *) node, &nlstate->jstate); + ExecAssignProjectionInfo((Plan *) node, &nlstate->jstate); + + /* ---------------- + * finally, wipe the current outer tuple clean. + * ---------------- + */ + nlstate->jstate.cs_OuterTupleSlot = NULL; + nlstate->jstate.cs_TupFromTlist = false; + + NL1_printf("ExecInitNestLoop: %s\n", + "node initialized"); + return TRUE; } int -ExecCountSlotsNestLoop(NestLoop *node) +ExecCountSlotsNestLoop(NestLoop * node) { - return ExecCountSlotsNode(outerPlan(node)) + - ExecCountSlotsNode(innerPlan(node)) + - NESTLOOP_NSLOTS; + return ExecCountSlotsNode(outerPlan(node)) + + ExecCountSlotsNode(innerPlan(node)) + + NESTLOOP_NSLOTS; } /* ---------------------------------------------------------------- - * ExecEndNestLoop - * - * closes down scans and frees allocated storage + * ExecEndNestLoop + * + * closes down scans and frees allocated storage * ---------------------------------------------------------------- */ void -ExecEndNestLoop(NestLoop *node) +ExecEndNestLoop(NestLoop * node) { - NestLoopState *nlstate; - - NL1_printf("ExecEndNestLoop: %s\n", - "ending node processing"); - - /* ---------------- - * get info from the node - * ---------------- - */ - nlstate = node->nlstate; - - /* ---------------- - * Free the projection info - * - * Note: we don't ExecFreeResultType(nlstate) - * because the rule manager depends on the tupType - * returned by ExecMain(). So for now, this - * is freed at end-transaction time. -cim 6/2/91 - * ---------------- - */ - ExecFreeProjectionInfo(&nlstate->jstate); - - /* ---------------- - * close down subplans - * ---------------- - */ - ExecEndNode(outerPlan((Plan *) node), (Plan*)node); - ExecEndNode(innerPlan((Plan *) node), (Plan*)node); - - /* ---------------- - * clean out the tuple table - * ---------------- - */ - ExecClearTuple(nlstate->jstate.cs_ResultTupleSlot); - - NL1_printf("ExecEndNestLoop: %s\n", - "node processing ended"); + NestLoopState *nlstate; + + NL1_printf("ExecEndNestLoop: %s\n", + "ending node processing"); + + /* ---------------- + * get info from the node + * ---------------- + */ + nlstate = node->nlstate; + + /* ---------------- + * Free the projection info + * + * Note: we don't ExecFreeResultType(nlstate) + * because the rule manager depends on the tupType + * returned by ExecMain(). So for now, this + * is freed at end-transaction time. -cim 6/2/91 + * ---------------- + */ + ExecFreeProjectionInfo(&nlstate->jstate); + + /* ---------------- + * close down subplans + * ---------------- + */ + ExecEndNode(outerPlan((Plan *) node), (Plan *) node); + ExecEndNode(innerPlan((Plan *) node), (Plan *) node); + + /* ---------------- + * clean out the tuple table + * ---------------- + */ + ExecClearTuple(nlstate->jstate.cs_ResultTupleSlot); + + NL1_printf("ExecEndNestLoop: %s\n", + "node processing ended"); } diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index f4553dcc7b7..743bd73f2b3 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -1,33 +1,33 @@ /*------------------------------------------------------------------------- * * nodeResult.c-- - * support for constant nodes needing special code. + * support for constant nodes needing special code. * * Copyright (c) 1994, Regents of the University of California * * - * DESCRIPTION + * DESCRIPTION * - * Example: in constant queries where no relations are scanned, - * the planner generates result nodes. Examples of such queries are: + * Example: in constant queries where no relations are scanned, + * the planner generates result nodes. Examples of such queries are: * - * retrieve (x = 1) - * and - * append emp (name = "mike", salary = 15000) + * retrieve (x = 1) + * and + * append emp (name = "mike", salary = 15000) * - * Result nodes are also used to optimise queries - * with tautological qualifications like: + * Result nodes are also used to optimise queries + * with tautological qualifications like: * - * retrieve (emp.all) where 2 > 1 + * retrieve (emp.all) where 2 > 1 * - * In this case, the plan generated is + * In this case, the plan generated is * - * Result (with 2 > 1 qual) - * / - * SeqScan (emp.all) + * Result (with 2 > 1 qual) + * / + * SeqScan (emp.all) * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.2 1996/10/31 10:12:18 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.3 1997/09/07 04:41:42 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -38,253 +38,259 @@ #include "executor/nodeResult.h" /* ---------------------------------------------------------------- - * ExecResult(node) + * ExecResult(node) * - * returns the tuples from the outer plan which satisify the - * qualification clause. Since result nodes with right - * subtrees are never planned, we ignore the right subtree - * entirely (for now).. -cim 10/7/89 + * returns the tuples from the outer plan which satisify the + * qualification clause. Since result nodes with right + * subtrees are never planned, we ignore the right subtree + * entirely (for now).. -cim 10/7/89 * - * The qualification containing only constant clauses are - * checked first before any processing is done. It always returns - * 'nil' if the constant qualification is not satisfied. + * The qualification containing only constant clauses are + * checked first before any processing is done. It always returns + * 'nil' if the constant qualification is not satisfied. * ---------------------------------------------------------------- */ TupleTableSlot * -ExecResult(Result *node) +ExecResult(Result * node) { - ResultState *resstate; - TupleTableSlot *outerTupleSlot; - TupleTableSlot *resultSlot; - Plan *outerPlan; - ExprContext *econtext; - Node *qual; - bool qualResult; - bool isDone; - ProjectionInfo *projInfo; - - /* ---------------- - * initialize the result node's state - * ---------------- - */ - resstate = node->resstate; - - /* ---------------- - * get the expression context - * ---------------- - */ - econtext = resstate->cstate.cs_ExprContext; - - /* ---------------- - * check tautological qualifications like (2 > 1) - * ---------------- - */ - qual = node->resconstantqual; - if (qual != NULL) { - qualResult = ExecQual((List*)qual, econtext); - /* ---------------- - * if we failed the constant qual, then there - * is no need to continue processing because regardless of - * what happens, the constant qual will be false.. - * ---------------- - */ - if (qualResult == false) - return NULL; - - /* ---------------- - * our constant qualification succeeded so now we - * throw away the qual because we know it will always - * succeed. - * ---------------- - */ - node->resconstantqual = NULL; - } - - if (resstate->cstate.cs_TupFromTlist) { + ResultState *resstate; + TupleTableSlot *outerTupleSlot; + TupleTableSlot *resultSlot; + Plan *outerPlan; + ExprContext *econtext; + Node *qual; + bool qualResult; + bool isDone; ProjectionInfo *projInfo; - - projInfo = resstate->cstate.cs_ProjInfo; - resultSlot = ExecProject(projInfo, &isDone); - if (!isDone) - return resultSlot; - } - - /* ---------------- - * retrieve a tuple that satisfy the qual from the outer plan until - * there are no more. - * - * if rs_done is 1 then it means that we were asked to return - * a constant tuple and we alread did the last time ExecResult() - * was called, so now we are through. - * ---------------- - */ - outerPlan = outerPlan(node); - - while (!resstate->rs_done) { /* ---------------- - * get next outer tuple if necessary. + * initialize the result node's state * ---------------- */ - if (outerPlan != NULL) { - outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node); - - if (TupIsNull(outerTupleSlot)) - return NULL; - - resstate->cstate.cs_OuterTupleSlot = outerTupleSlot; - } else { - - /* ---------------- - * if we don't have an outer plan, then it's probably - * the case that we are doing a retrieve or an append - * with a constant target list, so we should only return - * the constant tuple once or never if we fail the qual. - * ---------------- - */ - resstate->rs_done = 1; - } - + resstate = node->resstate; + /* ---------------- - * get the information to place into the expr context + * get the expression context * ---------------- */ - resstate = node->resstate; - - outerTupleSlot = resstate->cstate.cs_OuterTupleSlot; - + econtext = resstate->cstate.cs_ExprContext; + /* ---------------- - * fill in the information in the expression context - * XXX gross hack. use outer tuple as scan tuple + * check tautological qualifications like (2 > 1) * ---------------- */ - econtext->ecxt_outertuple = outerTupleSlot; - econtext->ecxt_scantuple = outerTupleSlot; - + qual = node->resconstantqual; + if (qual != NULL) + { + qualResult = ExecQual((List *) qual, econtext); + /* ---------------- + * if we failed the constant qual, then there + * is no need to continue processing because regardless of + * what happens, the constant qual will be false.. + * ---------------- + */ + if (qualResult == false) + return NULL; + + /* ---------------- + * our constant qualification succeeded so now we + * throw away the qual because we know it will always + * succeed. + * ---------------- + */ + node->resconstantqual = NULL; + } + + if (resstate->cstate.cs_TupFromTlist) + { + ProjectionInfo *projInfo; + + projInfo = resstate->cstate.cs_ProjInfo; + resultSlot = ExecProject(projInfo, &isDone); + if (!isDone) + return resultSlot; + } + /* ---------------- - * form the result tuple and pass it back using ExecProject() + * retrieve a tuple that satisfy the qual from the outer plan until + * there are no more. + * + * if rs_done is 1 then it means that we were asked to return + * a constant tuple and we alread did the last time ExecResult() + * was called, so now we are through. * ---------------- */ - projInfo = resstate->cstate.cs_ProjInfo; - resultSlot = ExecProject(projInfo, &isDone); - resstate->cstate.cs_TupFromTlist = !isDone; - return resultSlot; - } + outerPlan = outerPlan(node); + + while (!resstate->rs_done) + { + + /* ---------------- + * get next outer tuple if necessary. + * ---------------- + */ + if (outerPlan != NULL) + { + outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node); + + if (TupIsNull(outerTupleSlot)) + return NULL; - return NULL; + resstate->cstate.cs_OuterTupleSlot = outerTupleSlot; + } + else + { + + /* ---------------- + * if we don't have an outer plan, then it's probably + * the case that we are doing a retrieve or an append + * with a constant target list, so we should only return + * the constant tuple once or never if we fail the qual. + * ---------------- + */ + resstate->rs_done = 1; + } + + /* ---------------- + * get the information to place into the expr context + * ---------------- + */ + resstate = node->resstate; + + outerTupleSlot = resstate->cstate.cs_OuterTupleSlot; + + /* ---------------- + * fill in the information in the expression context + * XXX gross hack. use outer tuple as scan tuple + * ---------------- + */ + econtext->ecxt_outertuple = outerTupleSlot; + econtext->ecxt_scantuple = outerTupleSlot; + + /* ---------------- + * form the result tuple and pass it back using ExecProject() + * ---------------- + */ + projInfo = resstate->cstate.cs_ProjInfo; + resultSlot = ExecProject(projInfo, &isDone); + resstate->cstate.cs_TupFromTlist = !isDone; + return resultSlot; + } + + return NULL; } /* ---------------------------------------------------------------- - * ExecInitResult - * - * Creates the run-time state information for the result node - * produced by the planner and initailizes outer relations - * (child nodes). + * ExecInitResult + * + * Creates the run-time state information for the result node + * produced by the planner and initailizes outer relations + * (child nodes). * ---------------------------------------------------------------- */ bool -ExecInitResult(Result *node, EState *estate, Plan *parent) +ExecInitResult(Result * node, EState * estate, Plan * parent) { - ResultState *resstate; - - /* ---------------- - * assign execution state to node - * ---------------- - */ - node->plan.state = estate; - - /* ---------------- - * create new ResultState for node - * ---------------- - */ - resstate = makeNode(ResultState); - resstate->rs_done = 0; - node->resstate = resstate; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks and - * + create expression context for node - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &resstate->cstate, parent); - ExecAssignExprContext(estate, &resstate->cstate); - + ResultState *resstate; + + /* ---------------- + * assign execution state to node + * ---------------- + */ + node->plan.state = estate; + + /* ---------------- + * create new ResultState for node + * ---------------- + */ + resstate = makeNode(ResultState); + resstate->rs_done = 0; + node->resstate = resstate; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks and + * + create expression context for node + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &resstate->cstate, parent); + ExecAssignExprContext(estate, &resstate->cstate); + #define RESULT_NSLOTS 1 - /* ---------------- - * tuple table initialization - * ---------------- - */ - ExecInitResultTupleSlot(estate, &resstate->cstate); - - /* ---------------- - * then initialize children - * ---------------- - */ - ExecInitNode(outerPlan(node), estate, (Plan*)node); - - /* - * we don't use inner plan - */ - Assert(innerPlan(node)==NULL); - - /* ---------------- - * initialize tuple type and projection info - * ---------------- - */ - ExecAssignResultTypeFromTL((Plan*)node, &resstate->cstate); - ExecAssignProjectionInfo((Plan*)node, &resstate->cstate); - - /* ---------------- - * set "are we done yet" to false - * ---------------- - */ - resstate->rs_done = 0; - - return TRUE; + /* ---------------- + * tuple table initialization + * ---------------- + */ + ExecInitResultTupleSlot(estate, &resstate->cstate); + + /* ---------------- + * then initialize children + * ---------------- + */ + ExecInitNode(outerPlan(node), estate, (Plan *) node); + + /* + * we don't use inner plan + */ + Assert(innerPlan(node) == NULL); + + /* ---------------- + * initialize tuple type and projection info + * ---------------- + */ + ExecAssignResultTypeFromTL((Plan *) node, &resstate->cstate); + ExecAssignProjectionInfo((Plan *) node, &resstate->cstate); + + /* ---------------- + * set "are we done yet" to false + * ---------------- + */ + resstate->rs_done = 0; + + return TRUE; } int -ExecCountSlotsResult(Result *node) +ExecCountSlotsResult(Result * node) { - return ExecCountSlotsNode(outerPlan(node)) + RESULT_NSLOTS; + return ExecCountSlotsNode(outerPlan(node)) + RESULT_NSLOTS; } /* ---------------------------------------------------------------- - * ExecEndResult - * - * fees up storage allocated through C routines + * ExecEndResult + * + * fees up storage allocated through C routines * ---------------------------------------------------------------- */ void -ExecEndResult(Result *node) +ExecEndResult(Result * node) { - ResultState *resstate; - - resstate = node->resstate; - - /* ---------------- - * Free the projection info - * - * Note: we don't ExecFreeResultType(resstate) - * because the rule manager depends on the tupType - * returned by ExecMain(). So for now, this - * is freed at end-transaction time. -cim 6/2/91 - * ---------------- - */ - ExecFreeProjectionInfo(&resstate->cstate); - - /* ---------------- - * shut down subplans - * ---------------- - */ - ExecEndNode(outerPlan(node), (Plan*)node); - - /* ---------------- - * clean out the tuple table - * ---------------- - */ - ExecClearTuple(resstate->cstate.cs_ResultTupleSlot); + ResultState *resstate; + + resstate = node->resstate; + + /* ---------------- + * Free the projection info + * + * Note: we don't ExecFreeResultType(resstate) + * because the rule manager depends on the tupType + * returned by ExecMain(). So for now, this + * is freed at end-transaction time. -cim 6/2/91 + * ---------------- + */ + ExecFreeProjectionInfo(&resstate->cstate); + + /* ---------------- + * shut down subplans + * ---------------- + */ + ExecEndNode(outerPlan(node), (Plan *) node); + + /* ---------------- + * clean out the tuple table + * ---------------- + */ + ExecClearTuple(resstate->cstate.cs_ResultTupleSlot); } diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index b94bb58d260..d3451f8026f 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -1,25 +1,25 @@ /*------------------------------------------------------------------------- * * nodeSeqscan.c-- - * Support routines for sequential scans of relations. + * Support routines for sequential scans of relations. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.4 1997/08/19 21:31:12 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.5 1997/09/07 04:41:44 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * ExecSeqScan sequentially scans a relation. - * ExecSeqNext retrieve next tuple in sequential order. - * ExecInitSeqScan creates and initializes a seqscan node. - * ExecEndSeqScan releases any storage allocated. - * ExecSeqReScan rescans the relation - * ExecMarkPos marks scan position - * ExecRestrPos restores scan position + * ExecSeqScan sequentially scans a relation. + * ExecSeqNext retrieve next tuple in sequential order. + * ExecInitSeqScan creates and initializes a seqscan node. + * ExecEndSeqScan releases any storage allocated. + * ExecSeqReScan rescans the relation + * ExecMarkPos marks scan position + * ExecRestrPos restores scan position * */ #include "postgres.h" @@ -30,429 +30,443 @@ #include "access/heapam.h" #include "parser/parsetree.h" -static Oid InitScanRelation(SeqScan *node, EState *estate, - CommonScanState *scanstate, Plan *outerPlan); +static Oid +InitScanRelation(SeqScan * node, EState * estate, + CommonScanState * scanstate, Plan * outerPlan); -static TupleTableSlot *SeqNext(SeqScan *node); +static TupleTableSlot *SeqNext(SeqScan * node); /* ---------------------------------------------------------------- - * Scan Support + * Scan Support * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- - * SeqNext + * SeqNext * - * This is a workhorse for ExecSeqScan + * This is a workhorse for ExecSeqScan * ---------------------------------------------------------------- */ static TupleTableSlot * -SeqNext(SeqScan *node) +SeqNext(SeqScan * node) { - HeapTuple tuple; - HeapScanDesc scandesc; - CommonScanState *scanstate; - EState *estate; - ScanDirection direction; - TupleTableSlot *slot; - Buffer buffer; - - /* ---------------- - * get information from the estate and scan state - * ---------------- - */ - estate = node->plan.state; - scanstate = node->scanstate; - scandesc = scanstate->css_currentScanDesc; - direction = estate->es_direction; - - /* ---------------- - * get the next tuple from the access methods - * ---------------- - */ - tuple = heap_getnext(scandesc, /* scan desc */ - ScanDirectionIsBackward(direction), /*backward flag*/ - &buffer); /* return: buffer */ - - /* ---------------- - * save the tuple and the buffer returned to us by the access methods - * in our scan tuple slot and return the slot. Note: we pass 'false' - * because tuples returned by heap_getnext() are pointers onto - * disk pages and were not created with palloc() and so should not - * be pfree()'d. - * ---------------- - */ - slot = scanstate->css_ScanTupleSlot; - - slot = ExecStoreTuple(tuple, /* tuple to store */ - slot, /* slot to store in */ - buffer, /* buffer associated with this tuple */ - false); /* don't pfree this pointer */ - - /* ---------------- - * XXX -- mao says: The sequential scan for heap relations will - * automatically unpin the buffer this tuple is on when we cross - * a page boundary. The clearslot code also does this. We bump - * the pin count on the page here, since we actually have two - * pointers to it -- one in the scan desc and one in the tuple - * table slot. --mar 20 91 - * ---------------- - */ - ExecIncrSlotBufferRefcnt(slot); - - return slot; + HeapTuple tuple; + HeapScanDesc scandesc; + CommonScanState *scanstate; + EState *estate; + ScanDirection direction; + TupleTableSlot *slot; + Buffer buffer; + + /* ---------------- + * get information from the estate and scan state + * ---------------- + */ + estate = node->plan.state; + scanstate = node->scanstate; + scandesc = scanstate->css_currentScanDesc; + direction = estate->es_direction; + + /* ---------------- + * get the next tuple from the access methods + * ---------------- + */ + tuple = heap_getnext(scandesc, /* scan desc */ + ScanDirectionIsBackward(direction), /* backward flag */ + &buffer); /* return: buffer */ + + /* ---------------- + * save the tuple and the buffer returned to us by the access methods + * in our scan tuple slot and return the slot. Note: we pass 'false' + * because tuples returned by heap_getnext() are pointers onto + * disk pages and were not created with palloc() and so should not + * be pfree()'d. + * ---------------- + */ + slot = scanstate->css_ScanTupleSlot; + + slot = ExecStoreTuple(tuple,/* tuple to store */ + slot, /* slot to store in */ + buffer, /* buffer associated with this + * tuple */ + false); /* don't pfree this pointer */ + + /* ---------------- + * XXX -- mao says: The sequential scan for heap relations will + * automatically unpin the buffer this tuple is on when we cross + * a page boundary. The clearslot code also does this. We bump + * the pin count on the page here, since we actually have two + * pointers to it -- one in the scan desc and one in the tuple + * table slot. --mar 20 91 + * ---------------- + */ + ExecIncrSlotBufferRefcnt(slot); + + return slot; } /* ---------------------------------------------------------------- - * ExecSeqScan(node) + * ExecSeqScan(node) + * + * Scans the relation sequentially and returns the next qualifying + * tuple. + * It calls the ExecScan() routine and passes it the access method + * which retrieve tuples sequentially. * - * Scans the relation sequentially and returns the next qualifying - * tuple. - * It calls the ExecScan() routine and passes it the access method - * which retrieve tuples sequentially. - * */ TupleTableSlot * -ExecSeqScan(SeqScan *node) +ExecSeqScan(SeqScan * node) { - TupleTableSlot *slot; - Plan *outerPlan; - -S_printf("ExecSeqScan: scanning node: "); S_nodeDisplay(node); - - /* ---------------- - * if there is an outer subplan, get a tuple from it - * else, scan the relation - * ---------------- - */ - outerPlan = outerPlan((Plan *) node); - if (outerPlan) { - slot = ExecProcNode(outerPlan, (Plan*) node); - } else { - slot = ExecScan(node, SeqNext); - } - -S1_printf("ExecSeqScan: returned tuple slot: %d\n", slot); - - return slot; + TupleTableSlot *slot; + Plan *outerPlan; + + S_printf("ExecSeqScan: scanning node: "); + S_nodeDisplay(node); + + /* ---------------- + * if there is an outer subplan, get a tuple from it + * else, scan the relation + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + if (outerPlan) + { + slot = ExecProcNode(outerPlan, (Plan *) node); + } + else + { + slot = ExecScan(node, SeqNext); + } + + S1_printf("ExecSeqScan: returned tuple slot: %d\n", slot); + + return slot; } /* ---------------------------------------------------------------- - * InitScanRelation + * InitScanRelation * - * This does the initialization for scan relations and - * subplans of scans. + * This does the initialization for scan relations and + * subplans of scans. * ---------------------------------------------------------------- */ -static Oid -InitScanRelation(SeqScan *node, EState *estate, - CommonScanState *scanstate, Plan *outerPlan) +static Oid +InitScanRelation(SeqScan * node, EState * estate, + CommonScanState * scanstate, Plan * outerPlan) { - Index relid; - List *rangeTable; - RangeTblEntry *rtentry; - Oid reloid; - TimeQual timeQual; - ScanDirection direction; - Relation currentRelation; - HeapScanDesc currentScanDesc; - RelationInfo *resultRelationInfo; - - if (outerPlan == NULL) { - /* ---------------- - * if the outer node is nil then we are doing a simple - * sequential scan of a relation... - * - * get the relation object id from the relid'th entry - * in the range table, open that relation and initialize - * the scan state... - * ---------------- - */ - relid = node->scanrelid; - rangeTable = estate->es_range_table; - rtentry = rt_fetch(relid, rangeTable); - reloid = rtentry->relid; - timeQual = rtentry->timeQual; - direction = estate->es_direction; - resultRelationInfo = estate->es_result_relation_info; - - ExecOpenScanR(reloid, /* relation */ - 0, /* nkeys */ - NULL, /* scan key */ - 0, /* is index */ - direction, /* scan direction */ - timeQual, /* time qual */ - ¤tRelation, /* return: rel desc */ - (Pointer *) ¤tScanDesc); /* return: scan desc */ - - scanstate->css_currentRelation = currentRelation; - scanstate->css_currentScanDesc = currentScanDesc; - - ExecAssignScanType(scanstate, - RelationGetTupleDescriptor(currentRelation)); - } else { + Index relid; + List *rangeTable; + RangeTblEntry *rtentry; + Oid reloid; + TimeQual timeQual; + ScanDirection direction; + Relation currentRelation; + HeapScanDesc currentScanDesc; + RelationInfo *resultRelationInfo; + + if (outerPlan == NULL) + { + /* ---------------- + * if the outer node is nil then we are doing a simple + * sequential scan of a relation... + * + * get the relation object id from the relid'th entry + * in the range table, open that relation and initialize + * the scan state... + * ---------------- + */ + relid = node->scanrelid; + rangeTable = estate->es_range_table; + rtentry = rt_fetch(relid, rangeTable); + reloid = rtentry->relid; + timeQual = rtentry->timeQual; + direction = estate->es_direction; + resultRelationInfo = estate->es_result_relation_info; + + ExecOpenScanR(reloid, /* relation */ + 0, /* nkeys */ + NULL, /* scan key */ + 0, /* is index */ + direction,/* scan direction */ + timeQual, /* time qual */ + ¤tRelation, /* return: rel desc */ + (Pointer *) & currentScanDesc); /* return: scan desc */ + + scanstate->css_currentRelation = currentRelation; + scanstate->css_currentScanDesc = currentScanDesc; + + ExecAssignScanType(scanstate, + RelationGetTupleDescriptor(currentRelation)); + } + else + { + /* ---------------- + * otherwise we are scanning tuples from the + * outer subplan so we initialize the outer plan + * and nullify + * ---------------- + */ + ExecInitNode(outerPlan, estate, (Plan *) node); + + node->scanrelid = 0; + scanstate->css_currentRelation = NULL; + scanstate->css_currentScanDesc = NULL; + ExecAssignScanType(scanstate, NULL); + reloid = InvalidOid; + } + /* ---------------- - * otherwise we are scanning tuples from the - * outer subplan so we initialize the outer plan - * and nullify + * return the relation * ---------------- */ - ExecInitNode(outerPlan, estate, (Plan*)node); - - node->scanrelid = 0; - scanstate->css_currentRelation = NULL; - scanstate->css_currentScanDesc = NULL; - ExecAssignScanType(scanstate, NULL); - reloid = InvalidOid; - } - - /* ---------------- - * return the relation - * ---------------- - */ - return reloid; + return reloid; } /* ---------------------------------------------------------------- - * ExecInitSeqScan + * ExecInitSeqScan * * old comments - * Creates the run-time state information for the seqscan node - * and sets the relation id to contain relevant descriptors. - * - * If there is a outer subtree (sort), the outer subtree - * is initialized and the relation id is set to the descriptors - * returned by the subtree. + * Creates the run-time state information for the seqscan node + * and sets the relation id to contain relevant descriptors. + * + * If there is a outer subtree (sort), the outer subtree + * is initialized and the relation id is set to the descriptors + * returned by the subtree. * ---------------------------------------------------------------- */ bool -ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent) +ExecInitSeqScan(SeqScan * node, EState * estate, Plan * parent) { - CommonScanState *scanstate; - Plan *outerPlan; - Oid reloid; - HeapScanDesc scandesc; - - /* ---------------- - * assign the node's execution state - * ---------------- - */ - node->plan.state = estate; - - /* ---------------- - * create new CommonScanState for node - * ---------------- - */ - scanstate = makeNode(CommonScanState); - node->scanstate = scanstate; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + create expression context for node - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &scanstate->cstate, parent); - ExecAssignExprContext(estate, &scanstate->cstate); + CommonScanState *scanstate; + Plan *outerPlan; + Oid reloid; + HeapScanDesc scandesc; + + /* ---------------- + * assign the node's execution state + * ---------------- + */ + node->plan.state = estate; + + /* ---------------- + * create new CommonScanState for node + * ---------------- + */ + scanstate = makeNode(CommonScanState); + node->scanstate = scanstate; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + create expression context for node + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &scanstate->cstate, parent); + ExecAssignExprContext(estate, &scanstate->cstate); #define SEQSCAN_NSLOTS 3 - /* ---------------- - * tuple table initialization - * ---------------- - */ - ExecInitResultTupleSlot(estate, &scanstate->cstate); - ExecInitScanTupleSlot(estate, scanstate); - - /* ---------------- - * initialize scan relation or outer subplan - * ---------------- - */ - outerPlan = outerPlan((Plan *)node); - - reloid = InitScanRelation(node, estate, scanstate, outerPlan); - - scandesc = scanstate->css_currentScanDesc; - scanstate->cstate.cs_TupFromTlist = false; - - /* ---------------- - * initialize tuple type - * ---------------- - */ - ExecAssignResultTypeFromTL((Plan*)node, &scanstate->cstate); - ExecAssignProjectionInfo((Plan*)node, &scanstate->cstate); - - return TRUE; + /* ---------------- + * tuple table initialization + * ---------------- + */ + ExecInitResultTupleSlot(estate, &scanstate->cstate); + ExecInitScanTupleSlot(estate, scanstate); + + /* ---------------- + * initialize scan relation or outer subplan + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + + reloid = InitScanRelation(node, estate, scanstate, outerPlan); + + scandesc = scanstate->css_currentScanDesc; + scanstate->cstate.cs_TupFromTlist = false; + + /* ---------------- + * initialize tuple type + * ---------------- + */ + ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate); + ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate); + + return TRUE; } int -ExecCountSlotsSeqScan(SeqScan *node) +ExecCountSlotsSeqScan(SeqScan * node) { - return ExecCountSlotsNode(outerPlan(node)) + + return ExecCountSlotsNode(outerPlan(node)) + ExecCountSlotsNode(innerPlan(node)) + - SEQSCAN_NSLOTS; + SEQSCAN_NSLOTS; } /* ---------------------------------------------------------------- - * ExecEndSeqScan - * - * frees any storage allocated through C routines. - *| ...and also closes relations and/or shuts down outer subplan - *| -cim 8/14/89 + * ExecEndSeqScan + * + * frees any storage allocated through C routines. + *| ...and also closes relations and/or shuts down outer subplan + *| -cim 8/14/89 * ---------------------------------------------------------------- */ void -ExecEndSeqScan(SeqScan *node) +ExecEndSeqScan(SeqScan * node) { - CommonScanState *scanstate; - Plan *outerPlan; - - /* ---------------- - * get information from node - * ---------------- - */ - scanstate = node->scanstate; - - /* ---------------- - * Free the projection info and the scan attribute info - * - * Note: we don't ExecFreeResultType(scanstate) - * because the rule manager depends on the tupType - * returned by ExecMain(). So for now, this - * is freed at end-transaction time. -cim 6/2/91 - * ---------------- - */ - ExecFreeProjectionInfo(&scanstate->cstate); - - /* ---------------- - * close scan relation - * ---------------- - */ - ExecCloseR((Plan*) node); - - /* ---------------- - * clean up outer subtree (does nothing if there is no outerPlan) - * ---------------- - */ - outerPlan = outerPlan((Plan *)node); - ExecEndNode(outerPlan, (Plan*)node); - - /* ---------------- - * clean out the tuple table - * ---------------- - */ - ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot); - ExecClearTuple(scanstate->css_ScanTupleSlot); + CommonScanState *scanstate; + Plan *outerPlan; + + /* ---------------- + * get information from node + * ---------------- + */ + scanstate = node->scanstate; + + /* ---------------- + * Free the projection info and the scan attribute info + * + * Note: we don't ExecFreeResultType(scanstate) + * because the rule manager depends on the tupType + * returned by ExecMain(). So for now, this + * is freed at end-transaction time. -cim 6/2/91 + * ---------------- + */ + ExecFreeProjectionInfo(&scanstate->cstate); + + /* ---------------- + * close scan relation + * ---------------- + */ + ExecCloseR((Plan *) node); + + /* ---------------- + * clean up outer subtree (does nothing if there is no outerPlan) + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + ExecEndNode(outerPlan, (Plan *) node); + + /* ---------------- + * clean out the tuple table + * ---------------- + */ + ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot); + ExecClearTuple(scanstate->css_ScanTupleSlot); } /* ---------------------------------------------------------------- - * Join Support + * Join Support * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- - * ExecSeqReScan - * - * Rescans the relation. + * ExecSeqReScan + * + * Rescans the relation. * ---------------------------------------------------------------- */ void -ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan* parent) +ExecSeqReScan(SeqScan * node, ExprContext * exprCtxt, Plan * parent) { - CommonScanState *scanstate; - EState *estate; - Plan *outerPlan; - Relation rdesc; - HeapScanDesc sdesc; - ScanDirection direction; - - scanstate = node->scanstate; - estate = node->plan.state; - - outerPlan = outerPlan((Plan*)node); - if (outerPlan) { - /* we are scanning a subplan */ - outerPlan = outerPlan((Plan *)node); - ExecReScan(outerPlan, exprCtxt, parent); - } else { - /* otherwise, we are scanning a relation */ - rdesc = scanstate->css_currentRelation; - sdesc = scanstate->css_currentScanDesc; - direction = estate->es_direction; - sdesc = ExecReScanR(rdesc, sdesc, direction, 0, NULL); - scanstate->css_currentScanDesc = sdesc; - } + CommonScanState *scanstate; + EState *estate; + Plan *outerPlan; + Relation rdesc; + HeapScanDesc sdesc; + ScanDirection direction; + + scanstate = node->scanstate; + estate = node->plan.state; + + outerPlan = outerPlan((Plan *) node); + if (outerPlan) + { + /* we are scanning a subplan */ + outerPlan = outerPlan((Plan *) node); + ExecReScan(outerPlan, exprCtxt, parent); + } + else + { + /* otherwise, we are scanning a relation */ + rdesc = scanstate->css_currentRelation; + sdesc = scanstate->css_currentScanDesc; + direction = estate->es_direction; + sdesc = ExecReScanR(rdesc, sdesc, direction, 0, NULL); + scanstate->css_currentScanDesc = sdesc; + } } /* ---------------------------------------------------------------- - * ExecSeqMarkPos(node) - * - * Marks scan position. + * ExecSeqMarkPos(node) + * + * Marks scan position. * ---------------------------------------------------------------- */ void -ExecSeqMarkPos(SeqScan *node) +ExecSeqMarkPos(SeqScan * node) { - CommonScanState *scanstate; - Plan *outerPlan; - HeapScanDesc sdesc; - - scanstate = node->scanstate; - - /* ---------------- - * if we are scanning a subplan then propagate - * the ExecMarkPos() request to the subplan - * ---------------- - */ - outerPlan = outerPlan((Plan*)node); - if (outerPlan) { - ExecMarkPos(outerPlan); + CommonScanState *scanstate; + Plan *outerPlan; + HeapScanDesc sdesc; + + scanstate = node->scanstate; + + /* ---------------- + * if we are scanning a subplan then propagate + * the ExecMarkPos() request to the subplan + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + if (outerPlan) + { + ExecMarkPos(outerPlan); + return; + } + + /* ---------------- + * otherwise we are scanning a relation so mark the + * position using the access methods.. + * + * ---------------- + */ + sdesc = scanstate->css_currentScanDesc; + heap_markpos(sdesc); + return; - } - - /* ---------------- - * otherwise we are scanning a relation so mark the - * position using the access methods.. - * - * ---------------- - */ - sdesc = scanstate->css_currentScanDesc; - heap_markpos(sdesc); - - return; } /* ---------------------------------------------------------------- - * ExecSeqRestrPos - * - * Restores scan position. + * ExecSeqRestrPos + * + * Restores scan position. * ---------------------------------------------------------------- */ void -ExecSeqRestrPos(SeqScan *node) +ExecSeqRestrPos(SeqScan * node) { - CommonScanState *scanstate; - Plan *outerPlan; - HeapScanDesc sdesc; - - scanstate = node->scanstate; - - /* ---------------- - * if we are scanning a subplan then propagate - * the ExecRestrPos() request to the subplan - * ---------------- - */ - outerPlan = outerPlan((Plan*)node); - if (outerPlan) { - ExecRestrPos(outerPlan); - return; - } - - /* ---------------- - * otherwise we are scanning a relation so restore the - * position using the access methods.. - * ---------------- - */ - sdesc = scanstate->css_currentScanDesc; - heap_restrpos(sdesc); + CommonScanState *scanstate; + Plan *outerPlan; + HeapScanDesc sdesc; + + scanstate = node->scanstate; + + /* ---------------- + * if we are scanning a subplan then propagate + * the ExecRestrPos() request to the subplan + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + if (outerPlan) + { + ExecRestrPos(outerPlan); + return; + } + + /* ---------------- + * otherwise we are scanning a relation so restore the + * position using the access methods.. + * ---------------- + */ + sdesc = scanstate->css_currentScanDesc; + heap_restrpos(sdesc); } diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c index 0955108b2fb..eb2e2e7b180 100644 --- a/src/backend/executor/nodeSort.c +++ b/src/backend/executor/nodeSort.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * nodeSort.c-- - * Routines to handle sorting of relations. + * Routines to handle sorting of relations. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.6 1997/08/21 02:28:06 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.7 1997/09/07 04:41:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -25,383 +25,389 @@ #include "optimizer/internal.h" /* for _TEMP_RELATION_ID_ */ /* ---------------------------------------------------------------- - * FormSortKeys(node) - * - * Forms the structure containing information used to sort the relation. - * - * Returns an array of ScanKeyData. + * FormSortKeys(node) + * + * Forms the structure containing information used to sort the relation. + * + * Returns an array of ScanKeyData. * ---------------------------------------------------------------- */ -static ScanKey -FormSortKeys(Sort *sortnode) +static ScanKey +FormSortKeys(Sort * sortnode) { - ScanKey sortkeys; - List *targetList; - List *tl; - int keycount; - Resdom *resdom; - AttrNumber resno; - Index reskey; - Oid reskeyop; - - /* ---------------- - * get information from the node - * ---------------- - */ - targetList = sortnode->plan.targetlist; - keycount = sortnode->keycount; - - /* ---------------- - * first allocate space for scan keys - * ---------------- - */ - if (keycount <= 0) - elog(WARN, "FormSortKeys: keycount <= 0"); - sortkeys = (ScanKey) palloc(keycount * sizeof(ScanKeyData)); - - /* ---------------- - * form each scan key from the resdom info in the target list - * ---------------- - */ - foreach(tl, targetList) { - TargetEntry *target = (TargetEntry *)lfirst(tl); - resdom = target->resdom; - resno = resdom->resno; - reskey = resdom->reskey; - reskeyop = resdom->reskeyop; - - if (reskey > 0) { - ScanKeyEntryInitialize(&sortkeys[reskey-1], - 0, - resno, - (RegProcedure) DatumGetInt32(reskeyop), - (Datum) 0); + ScanKey sortkeys; + List *targetList; + List *tl; + int keycount; + Resdom *resdom; + AttrNumber resno; + Index reskey; + Oid reskeyop; + + /* ---------------- + * get information from the node + * ---------------- + */ + targetList = sortnode->plan.targetlist; + keycount = sortnode->keycount; + + /* ---------------- + * first allocate space for scan keys + * ---------------- + */ + if (keycount <= 0) + elog(WARN, "FormSortKeys: keycount <= 0"); + sortkeys = (ScanKey) palloc(keycount * sizeof(ScanKeyData)); + + /* ---------------- + * form each scan key from the resdom info in the target list + * ---------------- + */ + foreach(tl, targetList) + { + TargetEntry *target = (TargetEntry *) lfirst(tl); + + resdom = target->resdom; + resno = resdom->resno; + reskey = resdom->reskey; + reskeyop = resdom->reskeyop; + + if (reskey > 0) + { + ScanKeyEntryInitialize(&sortkeys[reskey - 1], + 0, + resno, + (RegProcedure) DatumGetInt32(reskeyop), + (Datum) 0); + } } - } - - return sortkeys; + + return sortkeys; } /* ---------------------------------------------------------------- - * ExecSort + * ExecSort * * old comments - * Sorts tuples from the outer subtree of the node in psort, - * which saves the results in a temporary file or memory. After the - * initial call, returns a tuple from the file with each call. - * Assumes that heap access method is used. - * - * Conditions: - * -- none. - * - * Initial States: - * -- the outer child is prepared to return the first tuple. + * Sorts tuples from the outer subtree of the node in psort, + * which saves the results in a temporary file or memory. After the + * initial call, returns a tuple from the file with each call. + * Assumes that heap access method is used. + * + * Conditions: + * -- none. + * + * Initial States: + * -- the outer child is prepared to return the first tuple. * ---------------------------------------------------------------- */ TupleTableSlot * -ExecSort(Sort *node) +ExecSort(Sort * node) { - EState *estate; - SortState *sortstate; - Plan *outerNode; - ScanDirection dir; - int keycount; - ScanKey sortkeys; - HeapTuple heapTuple; - TupleTableSlot *slot; - - /* ---------------- - * get state info from node - * ---------------- - */ - SO1_printf("ExecSort: %s\n", - "entering routine"); - - sortstate = node->sortstate; - estate = node->plan.state; - dir = estate->es_direction; - - /* ---------------- - * the first time we call this, psort sorts this into a file. - * Subsequent calls return tuples from psort. - * ---------------- - */ - - if (sortstate->sort_Flag == false) { - SO1_printf("ExecSort: %s\n", - "sortstate == false -> sorting subplan"); + EState *estate; + SortState *sortstate; + Plan *outerNode; + ScanDirection dir; + int keycount; + ScanKey sortkeys; + HeapTuple heapTuple; + TupleTableSlot *slot; + /* ---------------- - * set all relations to be scanned in the forward direction - * while creating the temporary relation. + * get state info from node * ---------------- */ - estate->es_direction = ForwardScanDirection; + SO1_printf("ExecSort: %s\n", + "entering routine"); + + sortstate = node->sortstate; + estate = node->plan.state; + dir = estate->es_direction; /* ---------------- - * prepare information for psort_begin() + * the first time we call this, psort sorts this into a file. + * Subsequent calls return tuples from psort. * ---------------- */ - outerNode = outerPlan((Plan *) node); - keycount = node->keycount; - sortkeys = (ScanKey)sortstate->sort_Keys; - SO1_printf("ExecSort: %s\n", - "calling psort_begin"); - - if (!psort_begin(node, /* this node */ - keycount, /* number keys */ - sortkeys)) /* keys */ + if (sortstate->sort_Flag == false) { - /* Psort says, there are no tuples to be sorted */ - return NULL; - } - - /* ---------------- - * restore to user specified direction - * ---------------- - */ - estate->es_direction = dir; - - /* ---------------- - * make sure the tuple descriptor is up to date - * ---------------- - */ - slot = (TupleTableSlot*)sortstate->csstate.cstate.cs_ResultTupleSlot; - /* *** get_cs_ResultTupleSlot((CommonState) sortstate); */ + SO1_printf("ExecSort: %s\n", + "sortstate == false -> sorting subplan"); + /* ---------------- + * set all relations to be scanned in the forward direction + * while creating the temporary relation. + * ---------------- + */ + estate->es_direction = ForwardScanDirection; + + /* ---------------- + * prepare information for psort_begin() + * ---------------- + */ + outerNode = outerPlan((Plan *) node); + + keycount = node->keycount; + sortkeys = (ScanKey) sortstate->sort_Keys; + SO1_printf("ExecSort: %s\n", + "calling psort_begin"); + + if (!psort_begin(node, /* this node */ + keycount, /* number keys */ + sortkeys)) /* keys */ + { + /* Psort says, there are no tuples to be sorted */ + return NULL; + } + + /* ---------------- + * restore to user specified direction + * ---------------- + */ + estate->es_direction = dir; - slot->ttc_tupleDescriptor = ExecGetTupType(outerNode); + /* ---------------- + * make sure the tuple descriptor is up to date + * ---------------- + */ + slot = (TupleTableSlot *) sortstate->csstate.cstate.cs_ResultTupleSlot; + /* *** get_cs_ResultTupleSlot((CommonState) sortstate); */ + + slot->ttc_tupleDescriptor = ExecGetTupType(outerNode); #if 0 - slot->ttc_execTupDescriptor = ExecGetExecTupDesc(outerNode); + slot->ttc_execTupDescriptor = ExecGetExecTupDesc(outerNode); #endif + /* ---------------- + * finally set the sorted flag to true + * ---------------- + */ + sortstate->sort_Flag = true; + SO1_printf(stderr, "ExecSort: sorting done.\n"); + } + else + { + slot = (TupleTableSlot *) sortstate->csstate.cstate.cs_ResultTupleSlot; + /* *** get_cs_ResultTupleSlot((CommonState) sortstate); */ +/* slot = sortstate->csstate.css_ScanTupleSlot; orig */ + } + + SO1_printf("ExecSort: %s\n", + "retrieving tuple from sorted relation"); + /* ---------------- - * finally set the sorted flag to true + * at this point we grab a tuple from psort * ---------------- */ - sortstate->sort_Flag = true; - SO1_printf(stderr, "ExecSort: sorting done.\n"); - } - else { - slot = (TupleTableSlot*)sortstate->csstate.cstate.cs_ResultTupleSlot; - /* *** get_cs_ResultTupleSlot((CommonState) sortstate); */ -/* slot = sortstate->csstate.css_ScanTupleSlot; orig */ - } - - SO1_printf("ExecSort: %s\n", - "retrieving tuple from sorted relation"); - - /* ---------------- - * at this point we grab a tuple from psort - * ---------------- - */ - heapTuple = psort_grabtuple(node); - - if (heapTuple == NULL) { -/* psort_end(node); */ - return (ExecClearTuple(slot)); - } - - ExecStoreTuple(heapTuple, /* tuple to store */ - slot, /* slot to store in */ - InvalidBuffer, /* no buffer */ - true); /* free the palloc'd tuple */ -/* printf("ExecSort: (%x)",node);print_slot(slot);printf("\n");*/ - return slot; + heapTuple = psort_grabtuple(node); + + if (heapTuple == NULL) + { +/* psort_end(node); */ + return (ExecClearTuple(slot)); + } + + ExecStoreTuple(heapTuple, /* tuple to store */ + slot, /* slot to store in */ + InvalidBuffer, /* no buffer */ + true); /* free the palloc'd tuple */ +/* printf("ExecSort: (%x)",node);print_slot(slot);printf("\n");*/ + return slot; #if 0 - return ExecStoreTuple(heapTuple, /* tuple to store */ - slot, /* slot to store in */ - InvalidBuffer, /* no buffer */ - true); /* free the palloc'd tuple */ + return ExecStoreTuple(heapTuple, /* tuple to store */ + slot, /* slot to store in */ + InvalidBuffer, /* no buffer */ + true);/* free the palloc'd tuple */ #endif } /* ---------------------------------------------------------------- - * ExecInitSort + * ExecInitSort * * old comments - * Creates the run-time state information for the sort node - * produced by the planner and initailizes its outer subtree. + * Creates the run-time state information for the sort node + * produced by the planner and initailizes its outer subtree. * ---------------------------------------------------------------- */ bool -ExecInitSort(Sort *node, EState *estate, Plan *parent) +ExecInitSort(Sort * node, EState * estate, Plan * parent) { - SortState *sortstate; - Plan *outerPlan; - ScanKey sortkeys; - - SO1_printf("ExecInitSort: %s\n", - "initializing sort node"); - - /* ---------------- - * assign the node's execution state - * ---------------- - */ - node->plan.state = estate; - - /* ---------------- - * create state structure - * ---------------- - */ - sortstate = makeNode(SortState); - sortstate->sort_Flag = 0; - sortstate->sort_Keys = NULL; - node->cleaned = FALSE; - - node->sortstate = sortstate; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks - * - * Sort nodes don't initialize their ExprContexts because - * they never call ExecQual or ExecTargetList. - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &sortstate->csstate.cstate, parent); - + SortState *sortstate; + Plan *outerPlan; + ScanKey sortkeys; + + SO1_printf("ExecInitSort: %s\n", + "initializing sort node"); + + /* ---------------- + * assign the node's execution state + * ---------------- + */ + node->plan.state = estate; + + /* ---------------- + * create state structure + * ---------------- + */ + sortstate = makeNode(SortState); + sortstate->sort_Flag = 0; + sortstate->sort_Keys = NULL; + node->cleaned = FALSE; + + node->sortstate = sortstate; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks + * + * Sort nodes don't initialize their ExprContexts because + * they never call ExecQual or ExecTargetList. + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &sortstate->csstate.cstate, parent); + #define SORT_NSLOTS 1 - /* ---------------- - * tuple table initialization - * - * sort nodes only return scan tuples from their sorted - * relation. - * ---------------- - */ - ExecInitResultTupleSlot(estate, &sortstate->csstate.cstate); - ExecInitScanTupleSlot(estate, &sortstate->csstate); - - /* ---------------- - * initializes child nodes - * ---------------- - */ - outerPlan = outerPlan((Plan *) node); - ExecInitNode(outerPlan, estate, (Plan *) node); - - /* ---------------- - * initialize sortstate information - * ---------------- - */ - sortkeys = FormSortKeys(node); - sortstate->sort_Keys = sortkeys; - sortstate->sort_Flag = false; - - /* ---------------- - * initialize tuple type. no need to initialize projection - * info because this node doesn't do projections. - * ---------------- - */ - ExecAssignResultTypeFromOuterPlan((Plan *)node, &sortstate->csstate.cstate); - ExecAssignScanTypeFromOuterPlan((Plan *) node, &sortstate->csstate); - sortstate->csstate.cstate.cs_ProjInfo = NULL; - - SO1_printf("ExecInitSort: %s\n", - "sort node initialized"); - - /* ---------------- - * return relation oid of temporary sort relation in a list - * (someday -- for now we return LispTrue... cim 10/12/89) - * ---------------- - */ - return TRUE; + /* ---------------- + * tuple table initialization + * + * sort nodes only return scan tuples from their sorted + * relation. + * ---------------- + */ + ExecInitResultTupleSlot(estate, &sortstate->csstate.cstate); + ExecInitScanTupleSlot(estate, &sortstate->csstate); + + /* ---------------- + * initializes child nodes + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + ExecInitNode(outerPlan, estate, (Plan *) node); + + /* ---------------- + * initialize sortstate information + * ---------------- + */ + sortkeys = FormSortKeys(node); + sortstate->sort_Keys = sortkeys; + sortstate->sort_Flag = false; + + /* ---------------- + * initialize tuple type. no need to initialize projection + * info because this node doesn't do projections. + * ---------------- + */ + ExecAssignResultTypeFromOuterPlan((Plan *) node, &sortstate->csstate.cstate); + ExecAssignScanTypeFromOuterPlan((Plan *) node, &sortstate->csstate); + sortstate->csstate.cstate.cs_ProjInfo = NULL; + + SO1_printf("ExecInitSort: %s\n", + "sort node initialized"); + + /* ---------------- + * return relation oid of temporary sort relation in a list + * (someday -- for now we return LispTrue... cim 10/12/89) + * ---------------- + */ + return TRUE; } int -ExecCountSlotsSort(Sort *node) +ExecCountSlotsSort(Sort * node) { - return ExecCountSlotsNode(outerPlan((Plan *)node)) + - ExecCountSlotsNode(innerPlan((Plan *)node)) + - SORT_NSLOTS; + return ExecCountSlotsNode(outerPlan((Plan *) node)) + + ExecCountSlotsNode(innerPlan((Plan *) node)) + + SORT_NSLOTS; } /* ---------------------------------------------------------------- - * ExecEndSort(node) + * ExecEndSort(node) * * old comments * ---------------------------------------------------------------- */ void -ExecEndSort(Sort *node) +ExecEndSort(Sort * node) { - SortState *sortstate; - Plan *outerPlan; - - /* ---------------- - * get info from the sort state - * ---------------- - */ - SO1_printf("ExecEndSort: %s\n", - "shutting down sort node"); - - sortstate = node->sortstate; - - /* ---------------- - * shut down the subplan - * ---------------- - */ - outerPlan = outerPlan((Plan *) node); - ExecEndNode(outerPlan, (Plan*)node); - - /* ---------------- - * clean out the tuple table - * ---------------- - */ - ExecClearTuple(sortstate->csstate.css_ScanTupleSlot); - - /* Clean up after psort */ - psort_end(node); - - SO1_printf("ExecEndSort: %s\n", - "sort node shutdown"); -} + SortState *sortstate; + Plan *outerPlan; + + /* ---------------- + * get info from the sort state + * ---------------- + */ + SO1_printf("ExecEndSort: %s\n", + "shutting down sort node"); + + sortstate = node->sortstate; + + /* ---------------- + * shut down the subplan + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + ExecEndNode(outerPlan, (Plan *) node); + + /* ---------------- + * clean out the tuple table + * ---------------- + */ + ExecClearTuple(sortstate->csstate.css_ScanTupleSlot); + + /* Clean up after psort */ + psort_end(node); + + SO1_printf("ExecEndSort: %s\n", + "sort node shutdown"); +} /* ---------------------------------------------------------------- - * ExecSortMarkPos + * ExecSortMarkPos * - * Calls psort to save the current position in the sorted file. + * Calls psort to save the current position in the sorted file. * ---------------------------------------------------------------- */ void -ExecSortMarkPos(Sort *node) +ExecSortMarkPos(Sort * node) { - SortState *sortstate; - - /* ---------------- - * if we haven't sorted yet, just return - * ---------------- - */ - sortstate = node->sortstate; - if (sortstate->sort_Flag == false) - return; - - psort_markpos(node); - - return; + SortState *sortstate; + + /* ---------------- + * if we haven't sorted yet, just return + * ---------------- + */ + sortstate = node->sortstate; + if (sortstate->sort_Flag == false) + return; + + psort_markpos(node); + + return; } /* ---------------------------------------------------------------- - * ExecSortRestrPos + * ExecSortRestrPos * - * Calls psort to restore the last saved sort file position. + * Calls psort to restore the last saved sort file position. * ---------------------------------------------------------------- */ void -ExecSortRestrPos(Sort *node) +ExecSortRestrPos(Sort * node) { - SortState *sortstate; - - /* ---------------- - * if we haven't sorted yet, just return. - * ---------------- - */ - sortstate = node->sortstate; - if (sortstate->sort_Flag == false) - return; - - /* ---------------- - * restore the scan to the previously marked position - * ---------------- - */ - psort_restorepos(node); + SortState *sortstate; + + /* ---------------- + * if we haven't sorted yet, just return. + * ---------------- + */ + sortstate = node->sortstate; + if (sortstate->sort_Flag == false) + return; + + /* ---------------- + * restore the scan to the previously marked position + * ---------------- + */ + psort_restorepos(node); } diff --git a/src/backend/executor/nodeTee.c b/src/backend/executor/nodeTee.c index 6ddbecc3a49..8a1e233125a 100644 --- a/src/backend/executor/nodeTee.c +++ b/src/backend/executor/nodeTee.c @@ -1,21 +1,21 @@ /*------------------------------------------------------------------------- * * nodeTee.c-- - * + * * * Copyright (c) 1994, Regents of the University of California * - * DESCRIPTION - * This code provides support for a tee node, which allows multiple - * parent in a megaplan. - * - * INTERFACE ROUTINES - * ExecTee - * ExecInitTee - * ExecEndTee + * DESCRIPTION + * This code provides support for a tee node, which allows multiple + * parent in a megaplan. + * + * INTERFACE ROUTINES + * ExecTee + * ExecInitTee + * ExecEndTee * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/Attic/nodeTee.c,v 1.6 1997/07/28 00:54:11 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/Attic/nodeTee.c,v 1.7 1997/09/07 04:41:46 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -25,9 +25,9 @@ #include "postgres.h" #include "utils/palloc.h" -#include "utils/relcache.h" +#include "utils/relcache.h" #include "utils/mcxt.h" -#include "storage/bufmgr.h" /* for IncrBufferRefCount */ +#include "storage/bufmgr.h" /* for IncrBufferRefCount */ #include "storage/smgr.h" #include "optimizer/internal.h" #include "executor/executor.h" @@ -38,475 +38,520 @@ #include "access/heapam.h" /* ------------------------------------------------------------------ - * ExecInitTee + * ExecInitTee * - * Create tee state + * Create tee state * * ------------------------------------------------------------------ */ bool -ExecInitTee(Tee* node, EState *currentEstate, Plan * parent) +ExecInitTee(Tee * node, EState * currentEstate, Plan * parent) { - TeeState *teeState; - Plan *outerPlan; - int len; - Relation bufferRel; - TupleDesc tupType; - EState *estate; - - /* it is possible that the Tee has already been initialized - since it can be reached by multiple parents. - If it is already initialized, simply return and do - not initialize the children nodes again - */ - if (node->plan.state) - return TRUE; - - /* ---------------- - * assign the node's execution state - * ---------------- - */ - /* make a new executor state, because we have a different - es_range_table */ - -/* node->plan.state = estate;*/ - - estate = CreateExecutorState(); - estate->es_direction = currentEstate->es_direction; - estate->es_BaseId = currentEstate->es_BaseId; - estate->es_BaseId = currentEstate->es_BaseId; - estate->es_tupleTable = currentEstate->es_tupleTable; - estate->es_refcount = currentEstate->es_refcount; - estate->es_junkFilter = currentEstate->es_junkFilter; - - /* use the range table for Tee subplan since the range tables - for the two parents may be different */ - if (node->rtentries) - estate->es_range_table = node->rtentries; - else - estate->es_range_table = currentEstate->es_range_table; - - node->plan.state = estate; - - - /* ---------------- - * create teeState structure - * ---------------- - */ - teeState = makeNode(TeeState); - teeState->tee_leftPlace = 0; - teeState->tee_rightPlace = 0; - teeState->tee_lastPlace = 0; - teeState->tee_bufferRel = NULL; - teeState->tee_leftScanDesc = NULL; - teeState->tee_rightScanDesc = NULL; - - - node->teestate = teeState; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks and - * + create expression context for node - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &(teeState->cstate), parent); - ExecAssignExprContext(estate, &(teeState->cstate)); + TeeState *teeState; + Plan *outerPlan; + int len; + Relation bufferRel; + TupleDesc tupType; + EState *estate; + + /* + * it is possible that the Tee has already been initialized since it + * can be reached by multiple parents. If it is already initialized, + * simply return and do not initialize the children nodes again + */ + if (node->plan.state) + return TRUE; + + /* ---------------- + * assign the node's execution state + * ---------------- + */ + + /* + * make a new executor state, because we have a different + * es_range_table + */ + +/* node->plan.state = estate;*/ + + estate = CreateExecutorState(); + estate->es_direction = currentEstate->es_direction; + estate->es_BaseId = currentEstate->es_BaseId; + estate->es_BaseId = currentEstate->es_BaseId; + estate->es_tupleTable = currentEstate->es_tupleTable; + estate->es_refcount = currentEstate->es_refcount; + estate->es_junkFilter = currentEstate->es_junkFilter; + + /* + * use the range table for Tee subplan since the range tables for the + * two parents may be different + */ + if (node->rtentries) + estate->es_range_table = node->rtentries; + else + estate->es_range_table = currentEstate->es_range_table; + + node->plan.state = estate; + + + /* ---------------- + * create teeState structure + * ---------------- + */ + teeState = makeNode(TeeState); + teeState->tee_leftPlace = 0; + teeState->tee_rightPlace = 0; + teeState->tee_lastPlace = 0; + teeState->tee_bufferRel = NULL; + teeState->tee_leftScanDesc = NULL; + teeState->tee_rightScanDesc = NULL; + + + node->teestate = teeState; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks and + * + create expression context for node + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &(teeState->cstate), parent); + ExecAssignExprContext(estate, &(teeState->cstate)); #define TEE_NSLOTS 2 - /* ---------------- - * initialize tuple slots - * ---------------- - */ - ExecInitResultTupleSlot(estate, &(teeState->cstate)); - - /* initialize child nodes */ - outerPlan = outerPlan((Plan*) node); - ExecInitNode(outerPlan, estate, (Plan*) node); - - /* ---------------- - * the tuple type info is from the outer plan of this node - * the result type is also the same as the outerplan - */ - ExecAssignResultTypeFromOuterPlan((Plan*) node, &(teeState->cstate)); - ExecAssignProjectionInfo((Plan*)node, &teeState->cstate); - - /* --------------------------------------- - initialize temporary relation to buffer tuples - */ - tupType = ExecGetResultType(&(teeState->cstate)); - len = ExecTargetListLength(((Plan*)node)->targetlist); - -/* bufferRel = ExecCreatR(len, tupType, _TEMP_RELATION_ID_); */ - - /* create a catalogued relation even though this is a temporary relation */ - /* cleanup of catalogued relations is easier to do */ - - if (node->teeTableName[0] != '\0') { - Relation r; - - teeState->tee_bufferRelname = pstrdup(node->teeTableName); - - /* we are given an tee table name, - if a relation by that name exists, then we open it, - else we create it and then open it */ - r = RelationNameGetRelation(teeState->tee_bufferRelname); - - if (RelationIsValid(r)) - bufferRel = heap_openr(teeState->tee_bufferRelname); + /* ---------------- + * initialize tuple slots + * ---------------- + */ + ExecInitResultTupleSlot(estate, &(teeState->cstate)); + + /* initialize child nodes */ + outerPlan = outerPlan((Plan *) node); + ExecInitNode(outerPlan, estate, (Plan *) node); + + /* ---------------- + * the tuple type info is from the outer plan of this node + * the result type is also the same as the outerplan + */ + ExecAssignResultTypeFromOuterPlan((Plan *) node, &(teeState->cstate)); + ExecAssignProjectionInfo((Plan *) node, &teeState->cstate); + + /* --------------------------------------- + initialize temporary relation to buffer tuples + */ + tupType = ExecGetResultType(&(teeState->cstate)); + len = ExecTargetListLength(((Plan *) node)->targetlist); + +/* bufferRel = ExecCreatR(len, tupType, _TEMP_RELATION_ID_); */ + + /* + * create a catalogued relation even though this is a temporary + * relation + */ + /* cleanup of catalogued relations is easier to do */ + + if (node->teeTableName[0] != '\0') + { + Relation r; + + teeState->tee_bufferRelname = pstrdup(node->teeTableName); + + /* + * we are given an tee table name, if a relation by that name + * exists, then we open it, else we create it and then open it + */ + r = RelationNameGetRelation(teeState->tee_bufferRelname); + + if (RelationIsValid(r)) + bufferRel = heap_openr(teeState->tee_bufferRelname); + else + bufferRel = heap_open(heap_create(teeState->tee_bufferRelname, + /*FIX */ NULL, + 'n', + DEFAULT_SMGR, + tupType)); + } else - bufferRel = heap_open(heap_create(teeState->tee_bufferRelname, -/*FIX */ NULL, - 'n', - DEFAULT_SMGR, - tupType)); - } - else { - sprintf(teeState->tee_bufferRelname, - "ttemp_%d", /* 'ttemp' for 'tee' temporary*/ - newoid()); -/* bufferRel = ExecCreatR(len, tupType, _TEMP_RELATION_ID); */ - bufferRel = heap_open(heap_create(teeState->tee_bufferRelname, - NULL, /*XXX */ - 'n', - DEFAULT_SMGR, - tupType)); - } - - teeState->tee_bufferRel = bufferRel; - - /*initialize a memory context for allocating thing like scan descriptors */ - /* we do this so that on cleanup of the tee, we can free things. - if we didn't have our own memory context, we would be in the memory - context of the portal that we happen to be using at the moment */ - - teeState->tee_mcxt = (MemoryContext)CreateGlobalMemory(teeState->tee_bufferRelname); - - /* don't initialize the scan descriptors here - because it's not good to initialize scan descriptors on empty - rels. Wait until the scan descriptors are needed - before initializing them. */ - - teeState->tee_leftScanDesc = NULL; - teeState->tee_rightScanDesc = NULL; - - return TRUE; + { + sprintf(teeState->tee_bufferRelname, + "ttemp_%d", /* 'ttemp' for 'tee' temporary */ + newoid()); +/* bufferRel = ExecCreatR(len, tupType, _TEMP_RELATION_ID); */ + bufferRel = heap_open(heap_create(teeState->tee_bufferRelname, + NULL, /* XXX */ + 'n', + DEFAULT_SMGR, + tupType)); + } + + teeState->tee_bufferRel = bufferRel; + + /* + * initialize a memory context for allocating thing like scan + * descriptors + */ + + /* + * we do this so that on cleanup of the tee, we can free things. if we + * didn't have our own memory context, we would be in the memory + * context of the portal that we happen to be using at the moment + */ + + teeState->tee_mcxt = (MemoryContext) CreateGlobalMemory(teeState->tee_bufferRelname); + + /* + * don't initialize the scan descriptors here because it's not good to + * initialize scan descriptors on empty rels. Wait until the scan + * descriptors are needed before initializing them. + */ + + teeState->tee_leftScanDesc = NULL; + teeState->tee_rightScanDesc = NULL; + + return TRUE; } -int -ExecCountSlotsTee(Tee *node) +int +ExecCountSlotsTee(Tee * node) { - /* Tee nodes can't have innerPlans */ - return ExecCountSlotsNode(outerPlan(node)) + TEE_NSLOTS; + /* Tee nodes can't have innerPlans */ + return ExecCountSlotsNode(outerPlan(node)) + TEE_NSLOTS; } /* ---------------------------------------------------------------- initTeeScanDescs - initializes the left and right scandescs on the temporary - relation of a Tee node + initializes the left and right scandescs on the temporary + relation of a Tee node - must open two separate scan descriptors, - because the left and right scans may be at different points + must open two separate scan descriptors, + because the left and right scans may be at different points * ---------------------------------------------------------------- */ -static void -initTeeScanDescs(Tee* node) +static void +initTeeScanDescs(Tee * node) { - TeeState *teeState; - Relation bufferRel; - ScanDirection dir; - MemoryContext orig; - - teeState = node->teestate; - if (teeState->tee_leftScanDesc && teeState->tee_rightScanDesc) - return; - - orig = CurrentMemoryContext; - MemoryContextSwitchTo(teeState->tee_mcxt); - - bufferRel = teeState->tee_bufferRel; - dir = ((Plan*)node)->state->es_direction; /* backwards not handled yet XXX */ - - if (teeState->tee_leftScanDesc == NULL) - { - teeState->tee_leftScanDesc = heap_beginscan(bufferRel, - ScanDirectionIsBackward(dir), - NowTimeQual, /* time qual */ - 0, /* num scan keys */ - NULL /* scan keys */ - ); - } - if (teeState->tee_rightScanDesc == NULL) - { - teeState->tee_rightScanDesc = heap_beginscan(bufferRel, - ScanDirectionIsBackward(dir), - NowTimeQual, /* time qual */ - 0, /* num scan keys */ - NULL /* scan keys */ - ); - } - - MemoryContextSwitchTo(orig); + TeeState *teeState; + Relation bufferRel; + ScanDirection dir; + MemoryContext orig; + + teeState = node->teestate; + if (teeState->tee_leftScanDesc && teeState->tee_rightScanDesc) + return; + + orig = CurrentMemoryContext; + MemoryContextSwitchTo(teeState->tee_mcxt); + + bufferRel = teeState->tee_bufferRel; + dir = ((Plan *) node)->state->es_direction; /* backwards not handled + * yet XXX */ + + if (teeState->tee_leftScanDesc == NULL) + { + teeState->tee_leftScanDesc = heap_beginscan(bufferRel, + ScanDirectionIsBackward(dir), + NowTimeQual, /* time qual */ + 0, /* num scan keys */ + NULL /* scan keys */ + ); + } + if (teeState->tee_rightScanDesc == NULL) + { + teeState->tee_rightScanDesc = heap_beginscan(bufferRel, + ScanDirectionIsBackward(dir), + NowTimeQual, /* time qual */ + 0, /* num scan keys */ + NULL /* scan keys */ + ); + } + + MemoryContextSwitchTo(orig); } /* ---------------------------------------------------------------- - * ExecTee(node) + * ExecTee(node) + * * + * A Tee serves to connect a subplan to multiple parents. + * the subplan is always the outplan of the Tee node. * - * A Tee serves to connect a subplan to multiple parents. - * the subplan is always the outplan of the Tee node. - * - * The Tee gets requests from either leftParent or rightParent, - * fetches the result tuple from the child, and then - * stored the result into a temporary relation (serving as a queue). - * leftPlace and rightPlace keep track of where the left and rightParents - * are. - * If a parent requests a tuple and that parent is not at the end - * of the temporary relation, then the request is satisfied from - * the queue instead of by executing the child plan + * The Tee gets requests from either leftParent or rightParent, + * fetches the result tuple from the child, and then + * stored the result into a temporary relation (serving as a queue). + * leftPlace and rightPlace keep track of where the left and rightParents + * are. + * If a parent requests a tuple and that parent is not at the end + * of the temporary relation, then the request is satisfied from + * the queue instead of by executing the child plan * * ---------------------------------------------------------------- */ -TupleTableSlot* -ExecTee(Tee *node, Plan *parent) +TupleTableSlot * +ExecTee(Tee * node, Plan * parent) { - EState *estate; - TeeState *teeState; - int leftPlace, rightPlace, lastPlace; - int branch; - TupleTableSlot* result; - TupleTableSlot* slot; - Plan *childNode; - ScanDirection dir; - HeapTuple heapTuple; - Relation bufferRel; - HeapScanDesc scanDesc; - Buffer buffer; - - estate = ((Plan*)node)->state; - teeState = node->teestate; - leftPlace = teeState->tee_leftPlace; - rightPlace = teeState->tee_rightPlace; - lastPlace = teeState->tee_lastPlace; - bufferRel = teeState->tee_bufferRel; - - childNode = outerPlan(node); - - dir = estate->es_direction; - - /* XXX doesn't handle backwards direction yet */ - - if (parent == node->leftParent) { - branch = leftPlace; - } - else - if ( (parent == node->rightParent) || (parent == (Plan*) node)) - /* the tee node could be the root node of the plan, - in which case, we treat it like a right-parent pull*/ + EState *estate; + TeeState *teeState; + int leftPlace, + rightPlace, + lastPlace; + int branch; + TupleTableSlot *result; + TupleTableSlot *slot; + Plan *childNode; + ScanDirection dir; + HeapTuple heapTuple; + Relation bufferRel; + HeapScanDesc scanDesc; + Buffer buffer; + + estate = ((Plan *) node)->state; + teeState = node->teestate; + leftPlace = teeState->tee_leftPlace; + rightPlace = teeState->tee_rightPlace; + lastPlace = teeState->tee_lastPlace; + bufferRel = teeState->tee_bufferRel; + + childNode = outerPlan(node); + + dir = estate->es_direction; + + /* XXX doesn't handle backwards direction yet */ + + if (parent == node->leftParent) + { + branch = leftPlace; + } + else if ((parent == node->rightParent) || (parent == (Plan *) node)) + + /* + * the tee node could be the root node of the plan, in which case, + * we treat it like a right-parent pull + */ + { + branch = rightPlace; + } + else + { + elog(WARN, "A Tee node can only be executed from its left or right parent\n"); + return NULL; + } + + if (branch == lastPlace) + { /* we're at the end of the queue already, + * - get a new tuple from the child plan, + * - store it in the queue, - increment + * lastPlace, - increment leftPlace or + * rightPlace as appropriate, - and return + * result */ + slot = ExecProcNode(childNode, (Plan *) node); + if (!TupIsNull(slot)) + { + heapTuple = slot->val; + + /* insert into temporary relation */ + heap_insert(bufferRel, heapTuple); + + /* + * once there is data in the temporary relation, ensure that + * the left and right scandescs are initialized + */ + initTeeScanDescs(node); + + scanDesc = (parent == node->leftParent) ? + teeState->tee_leftScanDesc : teeState->tee_rightScanDesc; + + { + + /* + * move the scandesc forward so we don't re-read this + * tuple later + */ + HeapTuple throwAway; + + /* Buffer buffer; */ + throwAway = heap_getnext(scanDesc, + ScanDirectionIsBackward(dir), + /* &buffer */ + (Buffer *) NULL); + } + + /* + * set the shouldFree field of the child's slot so that when + * the child's slot is free'd, this tuple isn't free'd also + */ + + /* + * does this mean this tuple has to be garbage collected + * later?? + */ + slot->ttc_shouldFree = false; + + teeState->tee_lastPlace = lastPlace + 1; + } + result = slot; + } + else + { /* the desired data already exists in the + * temporary relation */ + scanDesc = (parent == node->leftParent) ? + teeState->tee_leftScanDesc : teeState->tee_rightScanDesc; + + heapTuple = heap_getnext(scanDesc, + ScanDirectionIsBackward(dir), + &buffer); + + /* + * Increase the pin count on the buffer page, because the tuple + * stored in the slot also points to it (as well as the scan + * descriptor). If we don't, ExecStoreTuple will decrease the pin + * count on the next iteration. + */ + + if (buffer != InvalidBuffer) + IncrBufferRefCount(buffer); + + slot = teeState->cstate.cs_ResultTupleSlot; + slot->ttc_tupleDescriptor = RelationGetTupleDescriptor(bufferRel); + + result = ExecStoreTuple(heapTuple, /* tuple to store */ + slot, /* slot to store in */ + buffer, /* this tuple's buffer */ + false); /* don't free stuff from + * heap_getnext */ + + } + + if (parent == node->leftParent) { - branch = rightPlace; - } - else - { - elog(WARN,"A Tee node can only be executed from its left or right parent\n"); - return NULL; - } - - if (branch == lastPlace) - { /* we're at the end of the queue already, - - get a new tuple from the child plan, - - store it in the queue, - - increment lastPlace, - - increment leftPlace or rightPlace as appropriate, - - and return result - */ - slot = ExecProcNode(childNode, (Plan*)node); - if (!TupIsNull(slot)) - { - heapTuple = slot->val; - - /* insert into temporary relation */ - heap_insert(bufferRel, heapTuple); - - /* once there is data in the temporary relation, - ensure that the left and right scandescs are initialized */ - initTeeScanDescs(node); - - scanDesc = (parent == node->leftParent) ? - teeState->tee_leftScanDesc : teeState->tee_rightScanDesc; - - { - /* move the scandesc forward so we don't re-read this tuple later */ - HeapTuple throwAway; - /* Buffer buffer;*/ - throwAway = heap_getnext(scanDesc, - ScanDirectionIsBackward(dir), - /* &buffer */ - (Buffer*)NULL); - } - - /* set the shouldFree field of the child's slot so that - when the child's slot is free'd, this tuple isn't free'd also */ - /* does this mean this tuple has to be garbage collected later??*/ - slot->ttc_shouldFree = false; - - teeState->tee_lastPlace = lastPlace + 1; - } - result = slot; - } - else - {/* the desired data already exists in the temporary relation */ - scanDesc = (parent == node->leftParent) ? - teeState->tee_leftScanDesc : teeState->tee_rightScanDesc; - - heapTuple = heap_getnext(scanDesc, - ScanDirectionIsBackward(dir), - &buffer); - - /* Increase the pin count on the buffer page, because the - tuple stored in the slot also points to it (as well as - the scan descriptor). If we don't, ExecStoreTuple will - decrease the pin count on the next iteration. */ - - if (buffer != InvalidBuffer) - IncrBufferRefCount(buffer); - - slot = teeState->cstate.cs_ResultTupleSlot; - slot->ttc_tupleDescriptor = RelationGetTupleDescriptor(bufferRel); - - result = ExecStoreTuple(heapTuple,/* tuple to store */ - slot, /* slot to store in */ - buffer,/* this tuple's buffer */ - false); /* don't free stuff from heap_getnext */ - - } - - if (parent == node->leftParent) - { - teeState->tee_leftPlace = leftPlace+1; - } - else - { - teeState->tee_rightPlace = rightPlace+1; - } - - return result; + teeState->tee_leftPlace = leftPlace + 1; + } + else + { + teeState->tee_rightPlace = rightPlace + 1; + } + + return result; } /* ---------------------------------------------------------------- - * ExecTeeReScan(node) - * - * Rescans the relation. + * ExecTeeReScan(node) + * + * Rescans the relation. * ---------------------------------------------------------------- */ void -ExecTeeReScan(Tee *node, ExprContext *exprCtxt, Plan *parent) +ExecTeeReScan(Tee * node, ExprContext * exprCtxt, Plan * parent) { - EState *estate; - TeeState *teeState; - ScanDirection dir; + EState *estate; + TeeState *teeState; + ScanDirection dir; - estate = ((Plan*)node)->state; - teeState = node->teestate; + estate = ((Plan *) node)->state; + teeState = node->teestate; - dir = estate->es_direction; - - /* XXX doesn't handle backwards direction yet */ + dir = estate->es_direction; - if (parent == node->leftParent) { - if (teeState->tee_leftScanDesc) + /* XXX doesn't handle backwards direction yet */ + + if (parent == node->leftParent) + { + if (teeState->tee_leftScanDesc) + { + heap_rescan(teeState->tee_leftScanDesc, + ScanDirectionIsBackward(dir), + NULL); + teeState->tee_leftPlace = 0; + } + } + else { - heap_rescan(teeState->tee_leftScanDesc, - ScanDirectionIsBackward(dir), - NULL); - teeState->tee_leftPlace = 0; + if (teeState->tee_rightScanDesc) + { + heap_rescan(teeState->tee_leftScanDesc, + ScanDirectionIsBackward(dir), + NULL); + teeState->tee_rightPlace = 0; + } } - } - else - { - if (teeState->tee_rightScanDesc) - { - heap_rescan(teeState->tee_leftScanDesc, - ScanDirectionIsBackward(dir), - NULL); - teeState->tee_rightPlace = 0; - } - } } /* --------------------------------------------------------------------- - * ExecEndTee + * ExecEndTee * - * End the Tee node, and free up any storage + * End the Tee node, and free up any storage * since a Tee node can be downstream of multiple parent nodes, * only free when both parents are done * -------------------------------------------------------------------- */ -void -ExecEndTee(Tee* node, Plan* parent) +void +ExecEndTee(Tee * node, Plan * parent) { - EState *estate; - TeeState *teeState; - int leftPlace, rightPlace, lastPlace; - Relation bufferRel; - MemoryContext orig; - - estate = ((Plan*)node)->state; - teeState = node->teestate; - leftPlace = teeState->tee_leftPlace; - rightPlace = teeState->tee_rightPlace; - lastPlace = teeState->tee_lastPlace; - - if (!node->leftParent || parent == node->leftParent) - leftPlace = -1; - - if (!node->rightParent || parent == node->rightParent) - rightPlace = -1; - - if (parent == (Plan*)node) - rightPlace = leftPlace = -1; - - teeState->tee_leftPlace = leftPlace; - teeState->tee_rightPlace = rightPlace; - if ( (leftPlace == -1) && (rightPlace == -1) ) - { - /* remove the temporary relations */ - /* and close the scan descriptors */ - - bufferRel = teeState->tee_bufferRel; - if (bufferRel) { - heap_destroyr(bufferRel); - teeState->tee_bufferRel = NULL; - if (teeState->tee_mcxt) { - orig = CurrentMemoryContext; - MemoryContextSwitchTo(teeState->tee_mcxt); - } - else - orig = 0; - - if (teeState->tee_leftScanDesc) - { - heap_endscan(teeState->tee_leftScanDesc); - teeState->tee_leftScanDesc = NULL; - } - if (teeState->tee_rightScanDesc) - { - heap_endscan(teeState->tee_rightScanDesc); - teeState->tee_rightScanDesc = NULL; - } - - if (teeState->tee_mcxt) { - MemoryContextSwitchTo(orig); - teeState->tee_mcxt = NULL; - } + EState *estate; + TeeState *teeState; + int leftPlace, + rightPlace, + lastPlace; + Relation bufferRel; + MemoryContext orig; + + estate = ((Plan *) node)->state; + teeState = node->teestate; + leftPlace = teeState->tee_leftPlace; + rightPlace = teeState->tee_rightPlace; + lastPlace = teeState->tee_lastPlace; + + if (!node->leftParent || parent == node->leftParent) + leftPlace = -1; + + if (!node->rightParent || parent == node->rightParent) + rightPlace = -1; + + if (parent == (Plan *) node) + rightPlace = leftPlace = -1; + + teeState->tee_leftPlace = leftPlace; + teeState->tee_rightPlace = rightPlace; + if ((leftPlace == -1) && (rightPlace == -1)) + { + /* remove the temporary relations */ + /* and close the scan descriptors */ + + bufferRel = teeState->tee_bufferRel; + if (bufferRel) + { + heap_destroyr(bufferRel); + teeState->tee_bufferRel = NULL; + if (teeState->tee_mcxt) + { + orig = CurrentMemoryContext; + MemoryContextSwitchTo(teeState->tee_mcxt); + } + else + orig = 0; + + if (teeState->tee_leftScanDesc) + { + heap_endscan(teeState->tee_leftScanDesc); + teeState->tee_leftScanDesc = NULL; + } + if (teeState->tee_rightScanDesc) + { + heap_endscan(teeState->tee_rightScanDesc); + teeState->tee_rightScanDesc = NULL; + } + + if (teeState->tee_mcxt) + { + MemoryContextSwitchTo(orig); + teeState->tee_mcxt = NULL; + } + } } - } - -} +} diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c index 65c3bea76e0..75e40ccad96 100644 --- a/src/backend/executor/nodeUnique.c +++ b/src/backend/executor/nodeUnique.c @@ -1,25 +1,25 @@ /*------------------------------------------------------------------------- * * nodeUnique.c-- - * Routines to handle unique'ing of queries where appropriate + * Routines to handle unique'ing of queries where appropriate * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.7 1997/08/26 23:31:44 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.8 1997/09/07 04:41:50 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * ExecUnique - generate a unique'd temporary relation - * ExecInitUnique - initialize node and subnodes.. - * ExecEndUnique - shutdown node and subnodes + * ExecUnique - generate a unique'd temporary relation + * ExecInitUnique - initialize node and subnodes.. + * ExecEndUnique - shutdown node and subnodes * * NOTES - * Assumes tuples returned from subplan arrive in - * sorted order. + * Assumes tuples returned from subplan arrive in + * sorted order. * */ #include <string.h> @@ -31,296 +31,318 @@ #include "executor/nodeUnique.h" #include "optimizer/clauses.h" #include "access/heapam.h" -#include "access/printtup.h" /* for typtoout() */ -#include "utils/builtins.h" /* for namecpy()*/ +#include "access/printtup.h" /* for typtoout() */ +#include "utils/builtins.h" /* for namecpy() */ /* ---------------------------------------------------------------- - * ExecIdenticalTuples + * ExecIdenticalTuples * - * This is a hack function used by ExecUnique to see if - * two tuples are identical. This should be provided - * by the heap tuple code but isn't. The real problem - * is that we assume we can byte compare tuples to determine - * if they are "equal". In fact, if we have user defined - * types there may be problems because it's possible that - * an ADT may have multiple representations with the - * same ADT value. -cim + * This is a hack function used by ExecUnique to see if + * two tuples are identical. This should be provided + * by the heap tuple code but isn't. The real problem + * is that we assume we can byte compare tuples to determine + * if they are "equal". In fact, if we have user defined + * types there may be problems because it's possible that + * an ADT may have multiple representations with the + * same ADT value. -cim * ---------------------------------------------------------------- */ -static bool /* true if tuples are identical, false otherwise */ -ExecIdenticalTuples(TupleTableSlot *t1, TupleTableSlot *t2) +static bool /* true if tuples are identical, false + * otherwise */ +ExecIdenticalTuples(TupleTableSlot * t1, TupleTableSlot * t2) { - HeapTuple h1; - HeapTuple h2; - char *d1; - char *d2; - int len; - - h1 = t1->val; - h2 = t2->val; - - /* ---------------- - * if tuples aren't the same length then they are - * obviously different (one may have null attributes). - * ---------------- - */ - if (h1->t_len != h2->t_len) - return false; - - /* ---------------- - * if the tuples have different header offsets then - * they are different. This will prevent us from returning - * true when comparing tuples of one attribute where one of - * two we're looking at is null (t_len - t_hoff == 0). - * THE t_len FIELDS CAN BE THE SAME IN THIS CASE!! - * ---------------- - */ - if (h1->t_hoff != h2->t_hoff) - return false; - - /* ---------------- - * ok, now get the pointers to the data and the - * size of the attribute portion of the tuple. - * ---------------- - */ - d1 = (char *) GETSTRUCT(h1); - d2 = (char *) GETSTRUCT(h2); - len = (int) h1->t_len - (int) h1->t_hoff; - - /* ---------------- - * byte compare the data areas and return the result. - * ---------------- - */ - if (memcmp(d1, d2, len) != 0) - return false; - - return true; + HeapTuple h1; + HeapTuple h2; + char *d1; + char *d2; + int len; + + h1 = t1->val; + h2 = t2->val; + + /* ---------------- + * if tuples aren't the same length then they are + * obviously different (one may have null attributes). + * ---------------- + */ + if (h1->t_len != h2->t_len) + return false; + + /* ---------------- + * if the tuples have different header offsets then + * they are different. This will prevent us from returning + * true when comparing tuples of one attribute where one of + * two we're looking at is null (t_len - t_hoff == 0). + * THE t_len FIELDS CAN BE THE SAME IN THIS CASE!! + * ---------------- + */ + if (h1->t_hoff != h2->t_hoff) + return false; + + /* ---------------- + * ok, now get the pointers to the data and the + * size of the attribute portion of the tuple. + * ---------------- + */ + d1 = (char *) GETSTRUCT(h1); + d2 = (char *) GETSTRUCT(h2); + len = (int) h1->t_len - (int) h1->t_hoff; + + /* ---------------- + * byte compare the data areas and return the result. + * ---------------- + */ + if (memcmp(d1, d2, len) != 0) + return false; + + return true; } /* ---------------------------------------------------------------- - * ExecUnique + * ExecUnique * - * This is a very simple node which filters out duplicate - * tuples from a stream of sorted tuples from a subplan. + * This is a very simple node which filters out duplicate + * tuples from a stream of sorted tuples from a subplan. * - * XXX see comments below regarding freeing tuples. + * XXX see comments below regarding freeing tuples. * ---------------------------------------------------------------- */ -TupleTableSlot * /* return: a tuple or NULL */ -ExecUnique(Unique *node) +TupleTableSlot * /* return: a tuple or NULL */ +ExecUnique(Unique * node) { - UniqueState *uniquestate; - TupleTableSlot *resultTupleSlot; - TupleTableSlot *slot; - Plan *outerPlan; - char *uniqueAttr; - AttrNumber uniqueAttrNum; - TupleDesc tupDesc; - Oid typoutput; - - /* ---------------- - * get information from the node - * ---------------- - */ - uniquestate = node->uniquestate; - outerPlan = outerPlan((Plan *) node); - resultTupleSlot = uniquestate->cs_ResultTupleSlot; - uniqueAttr = node->uniqueAttr; - uniqueAttrNum = node->uniqueAttrNum; - - if (uniqueAttr) { - tupDesc = ExecGetResultType(uniquestate); - typoutput = typtoout((Oid)tupDesc->attrs[uniqueAttrNum-1]->atttypid); - } - else { /* keep compiler quiet */ - tupDesc = NULL; - typoutput = 0; - } - - /* ---------------- - * now loop, returning only non-duplicate tuples. - * We assume that the tuples arrive in sorted order - * so we can detect duplicates easily. - * ---------------- - */ - for (;;) { + UniqueState *uniquestate; + TupleTableSlot *resultTupleSlot; + TupleTableSlot *slot; + Plan *outerPlan; + char *uniqueAttr; + AttrNumber uniqueAttrNum; + TupleDesc tupDesc; + Oid typoutput; + /* ---------------- - * fetch a tuple from the outer subplan + * get information from the node * ---------------- */ - slot = ExecProcNode(outerPlan, (Plan*)node); - if (TupIsNull(slot)) - return NULL; - + uniquestate = node->uniquestate; + outerPlan = outerPlan((Plan *) node); + resultTupleSlot = uniquestate->cs_ResultTupleSlot; + uniqueAttr = node->uniqueAttr; + uniqueAttrNum = node->uniqueAttrNum; + + if (uniqueAttr) + { + tupDesc = ExecGetResultType(uniquestate); + typoutput = typtoout((Oid) tupDesc->attrs[uniqueAttrNum - 1]->atttypid); + } + else + { /* keep compiler quiet */ + tupDesc = NULL; + typoutput = 0; + } + /* ---------------- - * we use the result tuple slot to hold our saved tuples. - * if we haven't a saved tuple to compare our new tuple with, - * then we exit the loop. This new tuple as the saved tuple - * the next time we get here. + * now loop, returning only non-duplicate tuples. + * We assume that the tuples arrive in sorted order + * so we can detect duplicates easily. * ---------------- */ - if (TupIsNull(resultTupleSlot)) - break; - + for (;;) + { + /* ---------------- + * fetch a tuple from the outer subplan + * ---------------- + */ + slot = ExecProcNode(outerPlan, (Plan *) node); + if (TupIsNull(slot)) + return NULL; + + /* ---------------- + * we use the result tuple slot to hold our saved tuples. + * if we haven't a saved tuple to compare our new tuple with, + * then we exit the loop. This new tuple as the saved tuple + * the next time we get here. + * ---------------- + */ + if (TupIsNull(resultTupleSlot)) + break; + + /* ---------------- + * now test if the new tuple and the previous + * tuple match. If so then we loop back and fetch + * another new tuple from the subplan. + * ---------------- + */ + + if (uniqueAttr) + { + + /* + * to check equality, we check to see if the typoutput of the + * attributes are equal + */ + bool isNull1, + isNull2; + char *attr1, + *attr2; + char *val1, + *val2; + + attr1 = heap_getattr(slot->val, InvalidBuffer, + uniqueAttrNum, tupDesc, &isNull1); + attr2 = heap_getattr(resultTupleSlot->val, InvalidBuffer, + uniqueAttrNum, tupDesc, &isNull2); + + if (isNull1 == isNull2) + { + if (isNull1) /* both are null, they are equal */ + continue; + val1 = fmgr(typoutput, attr1, gettypelem(tupDesc->attrs[uniqueAttrNum - 1]->atttypid)); + val2 = fmgr(typoutput, attr2, gettypelem(tupDesc->attrs[uniqueAttrNum - 1]->atttypid)); + + /* + * now, val1 and val2 are ascii representations so we can + * use strcmp for comparison + */ + if (strcmp(val1, val2) == 0) /* they are equal */ + continue; + else + break; + } + else +/* one is null and the other isn't, they aren't equal */ + break; + + } + else + { + if (!ExecIdenticalTuples(slot, resultTupleSlot)) + break; + } + + } + /* ---------------- - * now test if the new tuple and the previous - * tuple match. If so then we loop back and fetch - * another new tuple from the subplan. + * we have a new tuple different from the previous saved tuple + * so we save it in the saved tuple slot. We copy the tuple + * so we don't increment the buffer ref count. * ---------------- */ + ExecStoreTuple(heap_copytuple(slot->val), + resultTupleSlot, + InvalidBuffer, + true); - if (uniqueAttr) { - /* to check equality, we check to see if the typoutput - of the attributes are equal */ - bool isNull1,isNull2; - char *attr1, *attr2; - char *val1, *val2; - - attr1 = heap_getattr(slot->val, InvalidBuffer, - uniqueAttrNum, tupDesc,&isNull1); - attr2 = heap_getattr(resultTupleSlot->val, InvalidBuffer, - uniqueAttrNum, tupDesc,&isNull2); - - if (isNull1 == isNull2) { - if (isNull1) /* both are null, they are equal */ - continue; - val1 = fmgr(typoutput, attr1, gettypelem(tupDesc->attrs[uniqueAttrNum-1]->atttypid)); - val2 = fmgr(typoutput, attr2, gettypelem(tupDesc->attrs[uniqueAttrNum-1]->atttypid)); - /* now, val1 and val2 are ascii representations so we can - use strcmp for comparison */ - if (strcmp(val1,val2) == 0) /* they are equal */ - continue; - else - break; - } - else /* one is null and the other isn't, they aren't equal */ - break; - - } - else { - if (! ExecIdenticalTuples(slot, resultTupleSlot)) - break; - } - - } - - /* ---------------- - * we have a new tuple different from the previous saved tuple - * so we save it in the saved tuple slot. We copy the tuple - * so we don't increment the buffer ref count. - * ---------------- - */ - ExecStoreTuple(heap_copytuple(slot->val), - resultTupleSlot, - InvalidBuffer, - true); - - return resultTupleSlot; + return resultTupleSlot; } /* ---------------------------------------------------------------- - * ExecInitUnique + * ExecInitUnique * - * This initializes the unique node state structures and - * the node's subplan. + * This initializes the unique node state structures and + * the node's subplan. * ---------------------------------------------------------------- */ -bool /* return: initialization status */ -ExecInitUnique(Unique *node, EState *estate, Plan *parent) +bool /* return: initialization status */ +ExecInitUnique(Unique * node, EState * estate, Plan * parent) { - UniqueState *uniquestate; - Plan *outerPlan; - char *uniqueAttr; - - /* ---------------- - * assign execution state to node - * ---------------- - */ - node->plan.state = estate; - - /* ---------------- - * create new UniqueState for node - * ---------------- - */ - uniquestate = makeNode(UniqueState); - node->uniquestate = uniquestate; - uniqueAttr = node->uniqueAttr; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks and - * - * Unique nodes have no ExprContext initialization because - * they never call ExecQual or ExecTargetList. - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, uniquestate, parent); - + UniqueState *uniquestate; + Plan *outerPlan; + char *uniqueAttr; + + /* ---------------- + * assign execution state to node + * ---------------- + */ + node->plan.state = estate; + + /* ---------------- + * create new UniqueState for node + * ---------------- + */ + uniquestate = makeNode(UniqueState); + node->uniquestate = uniquestate; + uniqueAttr = node->uniqueAttr; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks and + * + * Unique nodes have no ExprContext initialization because + * they never call ExecQual or ExecTargetList. + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, uniquestate, parent); + #define UNIQUE_NSLOTS 1 - /* ------------ - * Tuple table initialization - * ------------ - */ - ExecInitResultTupleSlot(estate, uniquestate); - - /* ---------------- - * then initialize outer plan - * ---------------- - */ - outerPlan = outerPlan((Plan *) node); - ExecInitNode(outerPlan, estate, (Plan *) node); - - /* ---------------- - * unique nodes do no projections, so initialize - * projection info for this node appropriately - * ---------------- - */ - ExecAssignResultTypeFromOuterPlan((Plan *)node,uniquestate); - uniquestate->cs_ProjInfo = NULL; - - if (uniqueAttr) { - TupleDesc tupDesc; - int i = 0; - - tupDesc = ExecGetResultType(uniquestate); - /* the parser should have ensured that uniqueAttr is a legal attribute name*/ - while ( strcmp((tupDesc->attrs[i]->attname).data, uniqueAttr) != 0) - i++; - node->uniqueAttrNum = i+1; /* attribute numbers start from 1 */ - } - else - node->uniqueAttrNum = InvalidAttrNumber; - - /* ---------------- - * all done. - * ---------------- - */ - return TRUE; + /* ------------ + * Tuple table initialization + * ------------ + */ + ExecInitResultTupleSlot(estate, uniquestate); + + /* ---------------- + * then initialize outer plan + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + ExecInitNode(outerPlan, estate, (Plan *) node); + + /* ---------------- + * unique nodes do no projections, so initialize + * projection info for this node appropriately + * ---------------- + */ + ExecAssignResultTypeFromOuterPlan((Plan *) node, uniquestate); + uniquestate->cs_ProjInfo = NULL; + + if (uniqueAttr) + { + TupleDesc tupDesc; + int i = 0; + + tupDesc = ExecGetResultType(uniquestate); + + /* + * the parser should have ensured that uniqueAttr is a legal + * attribute name + */ + while (strcmp((tupDesc->attrs[i]->attname).data, uniqueAttr) != 0) + i++; + node->uniqueAttrNum = i + 1; /* attribute numbers start from 1 */ + } + else + node->uniqueAttrNum = InvalidAttrNumber; + + /* ---------------- + * all done. + * ---------------- + */ + return TRUE; } int -ExecCountSlotsUnique(Unique *node) +ExecCountSlotsUnique(Unique * node) { - return ExecCountSlotsNode(outerPlan(node)) + + return ExecCountSlotsNode(outerPlan(node)) + ExecCountSlotsNode(innerPlan(node)) + - UNIQUE_NSLOTS; + UNIQUE_NSLOTS; } /* ---------------------------------------------------------------- - * ExecEndUnique + * ExecEndUnique * - * This shuts down the subplan and frees resources allocated - * to this node. + * This shuts down the subplan and frees resources allocated + * to this node. * ---------------------------------------------------------------- */ void -ExecEndUnique(Unique *node) +ExecEndUnique(Unique * node) { - UniqueState *uniquestate; - - uniquestate = node->uniquestate; - ExecEndNode(outerPlan((Plan *) node), (Plan*)node); - ExecClearTuple(uniquestate->cs_ResultTupleSlot); -} + UniqueState *uniquestate; + + uniquestate = node->uniquestate; + ExecEndNode(outerPlan((Plan *) node), (Plan *) node); + ExecClearTuple(uniquestate->cs_ResultTupleSlot); +} diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 74ace81171a..1d05a752d24 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * spi.c-- - * Server Programming Interface + * Server Programming Interface * *------------------------------------------------------------------------- */ @@ -9,440 +9,447 @@ #include "access/printtup.h" #include "fmgr.h" -typedef struct { - QueryTreeList *qtlist; /* malloced */ - uint32 processed; /* by Executor */ - SPITupleTable *tuptable; - Portal portal; /* portal per procedure */ - MemoryContext savedcxt; - CommandId savedId; -} _SPI_connection; - -static Portal _SPI_portal = (Portal) NULL; +typedef struct +{ + QueryTreeList *qtlist; /* malloced */ + uint32 processed; /* by Executor */ + SPITupleTable *tuptable; + Portal portal; /* portal per procedure */ + MemoryContext savedcxt; + CommandId savedId; +} _SPI_connection; + +static Portal _SPI_portal = (Portal) NULL; static _SPI_connection *_SPI_stack = NULL; static _SPI_connection *_SPI_current = NULL; -static int _SPI_connected = -1; -static int _SPI_curid = -1; +static int _SPI_connected = -1; +static int _SPI_curid = -1; + +uint32 SPI_processed = 0; +SPITupleTable *SPI_tuptable; +int SPI_result; -uint32 SPI_processed = 0; -SPITupleTable *SPI_tuptable; -int SPI_result; +void spi_printtup(HeapTuple tuple, TupleDesc tupdesc); -void spi_printtup (HeapTuple tuple, TupleDesc tupdesc); +typedef struct +{ + QueryTreeList *qtlist; + List *ptlist; + int nargs; + Oid *argtypes; +} _SPI_plan; -typedef struct { - QueryTreeList *qtlist; - List *ptlist; - int nargs; - Oid *argtypes; -} _SPI_plan; +static int _SPI_execute(char *src, int tcount, _SPI_plan * plan); +static int _SPI_pquery(QueryDesc * queryDesc, EState * state, int tcount); -static int _SPI_execute (char *src, int tcount, _SPI_plan *plan); -static int _SPI_pquery (QueryDesc *queryDesc, EState *state, int tcount); #if 0 -static void _SPI_fetch (FetchStmt *stmt); +static void _SPI_fetch(FetchStmt * stmt); + #endif -static int _SPI_execute_plan (_SPI_plan *plan, - char **Values, char *Nulls, int tcount); +static int +_SPI_execute_plan(_SPI_plan * plan, + char **Values, char *Nulls, int tcount); -static _SPI_plan *_SPI_copy_plan (_SPI_plan *plan, bool local); +static _SPI_plan *_SPI_copy_plan(_SPI_plan * plan, bool local); -static int _SPI_begin_call (bool execmem); -static int _SPI_end_call (bool procmem); -static MemoryContext _SPI_execmem (void); -static MemoryContext _SPI_procmem (void); -static bool _SPI_checktuples (bool isRetrieveIntoRelation); +static int _SPI_begin_call(bool execmem); +static int _SPI_end_call(bool procmem); +static MemoryContext _SPI_execmem(void); +static MemoryContext _SPI_procmem(void); +static bool _SPI_checktuples(bool isRetrieveIntoRelation); #ifdef SPI_EXECUTOR_STATS -extern int ShowExecutorStats; -extern void ResetUsage (void); -extern void ShowUsage (void); +extern int ShowExecutorStats; +extern void ResetUsage(void); +extern void ShowUsage(void); + #endif int -SPI_connect () +SPI_connect() { - char pname[64]; - PortalVariableMemory pvmem; - - /* - * It's possible on startup and after commit/abort. - * In future we'll catch commit/abort in some way... - */ - strcpy (pname, "<SPI manager>"); - _SPI_portal = GetPortalByName (pname); - if ( !PortalIsValid (_SPI_portal) ) - { - if ( _SPI_stack != NULL ) /* there was abort */ - free (_SPI_stack); - _SPI_current = _SPI_stack = NULL; - _SPI_connected = _SPI_curid = -1; - SPI_processed = 0; - SPI_tuptable = NULL; - _SPI_portal = CreatePortal (pname); - if ( !PortalIsValid (_SPI_portal) ) - elog (FATAL, "SPI_connect: global initialization failed"); - } - - /* - * When procedure called by Executor _SPI_curid expected to be - * equal to _SPI_connected - */ - if ( _SPI_curid != _SPI_connected ) - return (SPI_ERROR_CONNECT); - - if ( _SPI_stack == NULL ) - { - if ( _SPI_connected != -1 ) - elog (FATAL, "SPI_connect: no connection(s) expected"); - _SPI_stack = (_SPI_connection *) malloc (sizeof (_SPI_connection)); - } - else - { - if ( _SPI_connected <= -1 ) - elog (FATAL, "SPI_connect: some connection(s) expected"); - _SPI_stack = (_SPI_connection *) realloc (_SPI_stack, - (_SPI_connected + 1) * sizeof (_SPI_connection)); - } - /* - * We' returning to procedure where _SPI_curid == _SPI_connected - 1 - */ - _SPI_connected++; - - _SPI_current = &(_SPI_stack[_SPI_connected]); - _SPI_current->qtlist = NULL; - _SPI_current->processed = 0; - _SPI_current->tuptable = NULL; - - /* Create Portal for this procedure ... */ - sprintf (pname, "<SPI %d>", _SPI_connected); - _SPI_current->portal = CreatePortal (pname); - if ( !PortalIsValid (_SPI_current->portal) ) - elog (FATAL, "SPI_connect: initialization failed"); - - /* ... and switch to Portal' Variable memory - procedure' context */ - pvmem = PortalGetVariableMemory (_SPI_current->portal); - _SPI_current->savedcxt = MemoryContextSwitchTo ((MemoryContext)pvmem); - - _SPI_current->savedId = GetScanCommandId (); - SetScanCommandId (GetCurrentCommandId ()); - - return (SPI_OK_CONNECT); - + char pname[64]; + PortalVariableMemory pvmem; + + /* + * It's possible on startup and after commit/abort. In future we'll + * catch commit/abort in some way... + */ + strcpy(pname, "<SPI manager>"); + _SPI_portal = GetPortalByName(pname); + if (!PortalIsValid(_SPI_portal)) + { + if (_SPI_stack != NULL) /* there was abort */ + free(_SPI_stack); + _SPI_current = _SPI_stack = NULL; + _SPI_connected = _SPI_curid = -1; + SPI_processed = 0; + SPI_tuptable = NULL; + _SPI_portal = CreatePortal(pname); + if (!PortalIsValid(_SPI_portal)) + elog(FATAL, "SPI_connect: global initialization failed"); + } + + /* + * When procedure called by Executor _SPI_curid expected to be equal + * to _SPI_connected + */ + if (_SPI_curid != _SPI_connected) + return (SPI_ERROR_CONNECT); + + if (_SPI_stack == NULL) + { + if (_SPI_connected != -1) + elog(FATAL, "SPI_connect: no connection(s) expected"); + _SPI_stack = (_SPI_connection *) malloc(sizeof(_SPI_connection)); + } + else + { + if (_SPI_connected <= -1) + elog(FATAL, "SPI_connect: some connection(s) expected"); + _SPI_stack = (_SPI_connection *) realloc(_SPI_stack, + (_SPI_connected + 1) * sizeof(_SPI_connection)); + } + + /* + * We' returning to procedure where _SPI_curid == _SPI_connected - 1 + */ + _SPI_connected++; + + _SPI_current = &(_SPI_stack[_SPI_connected]); + _SPI_current->qtlist = NULL; + _SPI_current->processed = 0; + _SPI_current->tuptable = NULL; + + /* Create Portal for this procedure ... */ + sprintf(pname, "<SPI %d>", _SPI_connected); + _SPI_current->portal = CreatePortal(pname); + if (!PortalIsValid(_SPI_current->portal)) + elog(FATAL, "SPI_connect: initialization failed"); + + /* ... and switch to Portal' Variable memory - procedure' context */ + pvmem = PortalGetVariableMemory(_SPI_current->portal); + _SPI_current->savedcxt = MemoryContextSwitchTo((MemoryContext) pvmem); + + _SPI_current->savedId = GetScanCommandId(); + SetScanCommandId(GetCurrentCommandId()); + + return (SPI_OK_CONNECT); + } int -SPI_finish () +SPI_finish() { - int res; - - res = _SPI_begin_call (false); /* live in procedure memory */ - if ( res < 0 ) - return (res); - - /* Restore memory context as it was before procedure call */ - MemoryContextSwitchTo (_SPI_current->savedcxt); - PortalDestroy (&(_SPI_current->portal)); - - SetScanCommandId (_SPI_current->savedId); - - /* - * After _SPI_begin_call _SPI_connected == _SPI_curid. - * Now we are closing connection to SPI and returning to upper - * Executor and so _SPI_connected must be equal to _SPI_curid. - */ - _SPI_connected--; - _SPI_curid--; - if ( _SPI_connected == -1 ) - { - free (_SPI_stack); - _SPI_stack = NULL; - } - else - { - _SPI_stack = (_SPI_connection *) realloc (_SPI_stack, - (_SPI_connected + 1) * sizeof (_SPI_connection)); - _SPI_current = &(_SPI_stack[_SPI_connected]); - } - - return (SPI_OK_FINISH); - + int res; + + res = _SPI_begin_call(false); /* live in procedure memory */ + if (res < 0) + return (res); + + /* Restore memory context as it was before procedure call */ + MemoryContextSwitchTo(_SPI_current->savedcxt); + PortalDestroy(&(_SPI_current->portal)); + + SetScanCommandId(_SPI_current->savedId); + + /* + * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are + * closing connection to SPI and returning to upper Executor and so + * _SPI_connected must be equal to _SPI_curid. + */ + _SPI_connected--; + _SPI_curid--; + if (_SPI_connected == -1) + { + free(_SPI_stack); + _SPI_stack = NULL; + } + else + { + _SPI_stack = (_SPI_connection *) realloc(_SPI_stack, + (_SPI_connected + 1) * sizeof(_SPI_connection)); + _SPI_current = &(_SPI_stack[_SPI_connected]); + } + + return (SPI_OK_FINISH); + } int -SPI_exec (char *src, int tcount) +SPI_exec(char *src, int tcount) { - int res; - - if ( src == NULL || tcount < 0 ) - return (SPI_ERROR_ARGUMENT); - - res = _SPI_begin_call (true); - if ( res < 0 ) - return (res); - - res = _SPI_execute (src, tcount, NULL); - - _SPI_end_call (true); - return (res); + int res; + + if (src == NULL || tcount < 0) + return (SPI_ERROR_ARGUMENT); + + res = _SPI_begin_call(true); + if (res < 0) + return (res); + + res = _SPI_execute(src, tcount, NULL); + + _SPI_end_call(true); + return (res); } -int -SPI_execp (void *plan, char **Values, char *Nulls, int tcount) +int +SPI_execp(void *plan, char **Values, char *Nulls, int tcount) { - int res; - - if ( plan == NULL || tcount < 0 ) - return (SPI_ERROR_ARGUMENT); - - if ( ((_SPI_plan *)plan)->nargs > 0 && - ( Values == NULL || Nulls == NULL ) ) - return (SPI_ERROR_PARAM); - - res = _SPI_begin_call (true); - if ( res < 0 ) - return (res); - - res = _SPI_execute_plan ((_SPI_plan *)plan, Values, Nulls, tcount); - - _SPI_end_call (true); - return (res); + int res; + + if (plan == NULL || tcount < 0) + return (SPI_ERROR_ARGUMENT); + + if (((_SPI_plan *) plan)->nargs > 0 && + (Values == NULL || Nulls == NULL)) + return (SPI_ERROR_PARAM); + + res = _SPI_begin_call(true); + if (res < 0) + return (res); + + res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, tcount); + + _SPI_end_call(true); + return (res); } -void * -SPI_prepare (char *src, int nargs, Oid *argtypes) +void * +SPI_prepare(char *src, int nargs, Oid * argtypes) { - _SPI_plan *plan; - - if ( nargs < 0 || ( nargs > 0 && argtypes == NULL ) ) - { - SPI_result = SPI_ERROR_ARGUMENT; - return (NULL); - } - - SPI_result = _SPI_begin_call (true); - if ( SPI_result < 0 ) - return (NULL); - - plan = (_SPI_plan *) palloc (sizeof (_SPI_plan)); /* Executor context */ - plan->argtypes = argtypes; - plan->nargs = nargs; - - SPI_result = _SPI_execute (src, 0, plan); - - if ( SPI_result >= 0 ) /* copy plan to local space */ - plan = _SPI_copy_plan (plan, true); - else - plan = NULL; - - _SPI_end_call (true); - - return ((void *)plan); - + _SPI_plan *plan; + + if (nargs < 0 || (nargs > 0 && argtypes == NULL)) + { + SPI_result = SPI_ERROR_ARGUMENT; + return (NULL); + } + + SPI_result = _SPI_begin_call(true); + if (SPI_result < 0) + return (NULL); + + plan = (_SPI_plan *) palloc(sizeof(_SPI_plan)); /* Executor context */ + plan->argtypes = argtypes; + plan->nargs = nargs; + + SPI_result = _SPI_execute(src, 0, plan); + + if (SPI_result >= 0) /* copy plan to local space */ + plan = _SPI_copy_plan(plan, true); + else + plan = NULL; + + _SPI_end_call(true); + + return ((void *) plan); + } -void * -SPI_saveplan (void *plan) +void * +SPI_saveplan(void *plan) { - _SPI_plan *newplan; - - if ( plan == NULL ) - { - SPI_result = SPI_ERROR_ARGUMENT; - return (NULL); - } - - SPI_result = _SPI_begin_call (false); /* don't change context */ - if ( SPI_result < 0 ) - return (NULL); - - newplan = _SPI_copy_plan ((_SPI_plan *)plan, false); - - _SPI_curid--; - SPI_result = 0; - - return ((void *)newplan); - + _SPI_plan *newplan; + + if (plan == NULL) + { + SPI_result = SPI_ERROR_ARGUMENT; + return (NULL); + } + + SPI_result = _SPI_begin_call(false); /* don't change context */ + if (SPI_result < 0) + return (NULL); + + newplan = _SPI_copy_plan((_SPI_plan *) plan, false); + + _SPI_curid--; + SPI_result = 0; + + return ((void *) newplan); + } int -SPI_fnumber (TupleDesc tupdesc, char *fname) +SPI_fnumber(TupleDesc tupdesc, char *fname) { - int res; - - if ( _SPI_curid + 1 != _SPI_connected ) - return (SPI_ERROR_UNCONNECTED); - - for (res = 0; res < tupdesc->natts; res++) - { - if ( strcmp (tupdesc->attrs[res]->attname.data, fname) == 0 ) - return (res + 1); - } - - return (SPI_ERROR_NOATTRIBUTE); + int res; + + if (_SPI_curid + 1 != _SPI_connected) + return (SPI_ERROR_UNCONNECTED); + + for (res = 0; res < tupdesc->natts; res++) + { + if (strcmp(tupdesc->attrs[res]->attname.data, fname) == 0) + return (res + 1); + } + + return (SPI_ERROR_NOATTRIBUTE); } -char * -SPI_getvalue (HeapTuple tuple, TupleDesc tupdesc, int fnumber) +char * +SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber) { - char *val; - bool isnull; - Oid foutoid; - - SPI_result = 0; - if ( _SPI_curid + 1 != _SPI_connected ) - { - SPI_result = SPI_ERROR_UNCONNECTED; - return (NULL); - } - - if ( tuple->t_natts < fnumber || fnumber <= 0 ) - return (NULL); - - val = heap_getattr (tuple, InvalidBuffer, fnumber, tupdesc, &isnull); - if ( isnull ) - return (NULL); - foutoid = typtoout ((Oid) tupdesc->attrs[fnumber - 1]->atttypid); - if ( !OidIsValid (foutoid) ) - { - SPI_result = SPI_ERROR_NOOUTFUNC; - return (NULL); - } - - return (fmgr (foutoid, val, gettypelem (tupdesc->attrs[fnumber - 1]->atttypid))); + char *val; + bool isnull; + Oid foutoid; + + SPI_result = 0; + if (_SPI_curid + 1 != _SPI_connected) + { + SPI_result = SPI_ERROR_UNCONNECTED; + return (NULL); + } + + if (tuple->t_natts < fnumber || fnumber <= 0) + return (NULL); + + val = heap_getattr(tuple, InvalidBuffer, fnumber, tupdesc, &isnull); + if (isnull) + return (NULL); + foutoid = typtoout((Oid) tupdesc->attrs[fnumber - 1]->atttypid); + if (!OidIsValid(foutoid)) + { + SPI_result = SPI_ERROR_NOOUTFUNC; + return (NULL); + } + + return (fmgr(foutoid, val, gettypelem(tupdesc->attrs[fnumber - 1]->atttypid))); } -char * -SPI_getbinval (HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull) +char * +SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool * isnull) { - char *val; - - *isnull = true; - SPI_result = 0; - if ( _SPI_curid + 1 != _SPI_connected ) - { - SPI_result = SPI_ERROR_UNCONNECTED; - return (NULL); - } - - if ( tuple->t_natts < fnumber || fnumber <= 0 ) - return (NULL); - - val = heap_getattr (tuple, InvalidBuffer, fnumber, tupdesc, isnull); - - return (val); + char *val; + + *isnull = true; + SPI_result = 0; + if (_SPI_curid + 1 != _SPI_connected) + { + SPI_result = SPI_ERROR_UNCONNECTED; + return (NULL); + } + + if (tuple->t_natts < fnumber || fnumber <= 0) + return (NULL); + + val = heap_getattr(tuple, InvalidBuffer, fnumber, tupdesc, isnull); + + return (val); } -char * -SPI_gettype (TupleDesc tupdesc, int fnumber) +char * +SPI_gettype(TupleDesc tupdesc, int fnumber) { - HeapTuple typeTuple; - - SPI_result = 0; - if ( _SPI_curid + 1 != _SPI_connected ) - { - SPI_result = SPI_ERROR_UNCONNECTED; - return (NULL); - } - - if ( tupdesc->natts < fnumber || fnumber <= 0 ) - { - SPI_result = SPI_ERROR_NOATTRIBUTE; - return (NULL); - } - - typeTuple = SearchSysCacheTuple (TYPOID, - ObjectIdGetDatum (tupdesc->attrs[fnumber - 1]->atttypid), - 0, 0, 0); - - if ( !HeapTupleIsValid (typeTuple) ) - { - SPI_result = SPI_ERROR_TYPUNKNOWN; - return (NULL); - } - - return (pstrdup (((TypeTupleForm) GETSTRUCT (typeTuple))->typname.data)); + HeapTuple typeTuple; + + SPI_result = 0; + if (_SPI_curid + 1 != _SPI_connected) + { + SPI_result = SPI_ERROR_UNCONNECTED; + return (NULL); + } + + if (tupdesc->natts < fnumber || fnumber <= 0) + { + SPI_result = SPI_ERROR_NOATTRIBUTE; + return (NULL); + } + + typeTuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(tupdesc->attrs[fnumber - 1]->atttypid), + 0, 0, 0); + + if (!HeapTupleIsValid(typeTuple)) + { + SPI_result = SPI_ERROR_TYPUNKNOWN; + return (NULL); + } + + return (pstrdup(((TypeTupleForm) GETSTRUCT(typeTuple))->typname.data)); } Oid -SPI_gettypeid (TupleDesc tupdesc, int fnumber) +SPI_gettypeid(TupleDesc tupdesc, int fnumber) { - - SPI_result = 0; - if ( _SPI_curid + 1 != _SPI_connected ) - { - SPI_result = SPI_ERROR_UNCONNECTED; - return (InvalidOid); - } - - if ( tupdesc->natts < fnumber || fnumber <= 0 ) - { - SPI_result = SPI_ERROR_NOATTRIBUTE; - return (InvalidOid); - } - - return (tupdesc->attrs[fnumber - 1]->atttypid); + + SPI_result = 0; + if (_SPI_curid + 1 != _SPI_connected) + { + SPI_result = SPI_ERROR_UNCONNECTED; + return (InvalidOid); + } + + if (tupdesc->natts < fnumber || fnumber <= 0) + { + SPI_result = SPI_ERROR_NOATTRIBUTE; + return (InvalidOid); + } + + return (tupdesc->attrs[fnumber - 1]->atttypid); } -char * -SPI_getrelname (Relation rel) +char * +SPI_getrelname(Relation rel) { - - SPI_result = 0; - if ( _SPI_curid + 1 != _SPI_connected ) - { - SPI_result = SPI_ERROR_UNCONNECTED; - return (NULL); - } - - return (pstrdup (rel->rd_rel->relname.data)); + + SPI_result = 0; + if (_SPI_curid + 1 != _SPI_connected) + { + SPI_result = SPI_ERROR_UNCONNECTED; + return (NULL); + } + + return (pstrdup(rel->rd_rel->relname.data)); } /* * spi_printtup -- - * store tuple retrieved by Executor into SPITupleTable - * of current SPI procedure + * store tuple retrieved by Executor into SPITupleTable + * of current SPI procedure * */ void -spi_printtup (HeapTuple tuple, TupleDesc tupdesc) +spi_printtup(HeapTuple tuple, TupleDesc tupdesc) { - SPITupleTable *tuptable; - MemoryContext oldcxt; - - /* - * When called by Executor _SPI_curid expected to be - * equal to _SPI_connected - */ - if ( _SPI_curid != _SPI_connected || _SPI_connected < 0 ) - elog (FATAL, "SPI: improper call to spi_printtup"); - if ( _SPI_current != &(_SPI_stack[_SPI_curid]) ) - elog (FATAL, "SPI: stack corrupted in spi_printtup"); - - oldcxt = _SPI_procmem (); /* switch to procedure memory context */ - - tuptable = _SPI_current->tuptable; - if ( tuptable == NULL ) - { - _SPI_current->tuptable = tuptable = (SPITupleTable *) - palloc (sizeof (SPITupleTable)); - tuptable->alloced = tuptable->free = 128; - tuptable->vals = (HeapTuple *) palloc (tuptable->alloced * sizeof (HeapTuple)); - tuptable->tupdesc = CreateTupleDescCopy (tupdesc); - } - else if ( tuptable->free == 0 ) - { - tuptable->free = 256; - tuptable->alloced += tuptable->free; - tuptable->vals = (HeapTuple *) repalloc (tuptable->vals, - tuptable->alloced * sizeof (HeapTuple)); - } - - tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple (tuple); - (tuptable->free)--; - - MemoryContextSwitchTo (oldcxt); - return; + SPITupleTable *tuptable; + MemoryContext oldcxt; + + /* + * When called by Executor _SPI_curid expected to be equal to + * _SPI_connected + */ + if (_SPI_curid != _SPI_connected || _SPI_connected < 0) + elog(FATAL, "SPI: improper call to spi_printtup"); + if (_SPI_current != &(_SPI_stack[_SPI_curid])) + elog(FATAL, "SPI: stack corrupted in spi_printtup"); + + oldcxt = _SPI_procmem(); /* switch to procedure memory context */ + + tuptable = _SPI_current->tuptable; + if (tuptable == NULL) + { + _SPI_current->tuptable = tuptable = (SPITupleTable *) + palloc(sizeof(SPITupleTable)); + tuptable->alloced = tuptable->free = 128; + tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple)); + tuptable->tupdesc = CreateTupleDescCopy(tupdesc); + } + else if (tuptable->free == 0) + { + tuptable->free = 256; + tuptable->alloced += tuptable->free; + tuptable->vals = (HeapTuple *) repalloc(tuptable->vals, + tuptable->alloced * sizeof(HeapTuple)); + } + + tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple); + (tuptable->free)--; + + MemoryContextSwitchTo(oldcxt); + return; } /* @@ -450,330 +457,334 @@ spi_printtup (HeapTuple tuple, TupleDesc tupdesc) */ static int -_SPI_execute (char *src, int tcount, _SPI_plan *plan) +_SPI_execute(char *src, int tcount, _SPI_plan * plan) { - QueryTreeList *queryTree_list; - List *planTree_list; - List *ptlist; - QueryDesc *qdesc; - Query *queryTree; - Plan *planTree; - EState *state; - int qlen; - int nargs = 0; - Oid *argtypes = NULL; - int res; - int i; - - /* Increment CommandCounter to see changes made by now */ - CommandCounterIncrement (); - - SPI_processed = 0; - SPI_tuptable = NULL; - _SPI_current->tuptable = NULL; - _SPI_current->qtlist = NULL; - - if ( plan ) - { - nargs = plan->nargs; - argtypes = plan->argtypes; - } - ptlist = planTree_list = (List *) - pg_plan (src, argtypes, nargs, &queryTree_list, None); - - _SPI_current->qtlist = queryTree_list; - - qlen = queryTree_list->len; - for (i=0; ;i++) - { - queryTree = (Query*) (queryTree_list->qtrees[i]); - planTree = lfirst(planTree_list); - - planTree_list = lnext (planTree_list); - - if ( queryTree->commandType == CMD_UTILITY ) - { - if ( nodeTag (queryTree->utilityStmt ) == T_CopyStmt ) - { - CopyStmt *stmt = (CopyStmt *)(queryTree->utilityStmt); - - if ( stmt->filename == NULL ) - return (SPI_ERROR_COPY); - } - else if ( nodeTag (queryTree->utilityStmt ) == T_ClosePortalStmt || - nodeTag (queryTree->utilityStmt ) == T_FetchStmt ) - return (SPI_ERROR_CURSOR); - else if ( nodeTag (queryTree->utilityStmt ) == T_TransactionStmt ) - return (SPI_ERROR_TRANSACTION); - res = SPI_OK_UTILITY; - if ( plan == NULL ) - { - ProcessUtility (queryTree->utilityStmt, None); - if ( i < qlen - 1 ) - CommandCounterIncrement (); - else - return (res); - } - else if ( i >= qlen - 1 ) - break; - } - else if ( plan == NULL ) - { - qdesc = CreateQueryDesc (queryTree, planTree, - ( i < qlen - 1 ) ? None : SPI); - state = CreateExecutorState(); - res = _SPI_pquery (qdesc, state, ( i < qlen - 1 ) ? 0 : tcount); - if ( res < 0 || i >= qlen - 1 ) - return (res); - CommandCounterIncrement (); + QueryTreeList *queryTree_list; + List *planTree_list; + List *ptlist; + QueryDesc *qdesc; + Query *queryTree; + Plan *planTree; + EState *state; + int qlen; + int nargs = 0; + Oid *argtypes = NULL; + int res; + int i; + + /* Increment CommandCounter to see changes made by now */ + CommandCounterIncrement(); + + SPI_processed = 0; + SPI_tuptable = NULL; + _SPI_current->tuptable = NULL; + _SPI_current->qtlist = NULL; + + if (plan) + { + nargs = plan->nargs; + argtypes = plan->argtypes; } - else + ptlist = planTree_list = (List *) + pg_plan(src, argtypes, nargs, &queryTree_list, None); + + _SPI_current->qtlist = queryTree_list; + + qlen = queryTree_list->len; + for (i = 0;; i++) { - qdesc = CreateQueryDesc (queryTree, planTree, - ( i < qlen - 1 ) ? None : SPI); - res = _SPI_pquery (qdesc, NULL, ( i < qlen - 1 ) ? 0 : tcount); - if ( res < 0 ) - return (res); - if ( i >= qlen - 1 ) - break; - } - } - - plan->qtlist = queryTree_list; - plan->ptlist = ptlist; - - return (res); - + queryTree = (Query *) (queryTree_list->qtrees[i]); + planTree = lfirst(planTree_list); + + planTree_list = lnext(planTree_list); + + if (queryTree->commandType == CMD_UTILITY) + { + if (nodeTag(queryTree->utilityStmt) == T_CopyStmt) + { + CopyStmt *stmt = (CopyStmt *) (queryTree->utilityStmt); + + if (stmt->filename == NULL) + return (SPI_ERROR_COPY); + } + else if (nodeTag(queryTree->utilityStmt) == T_ClosePortalStmt || + nodeTag(queryTree->utilityStmt) == T_FetchStmt) + return (SPI_ERROR_CURSOR); + else if (nodeTag(queryTree->utilityStmt) == T_TransactionStmt) + return (SPI_ERROR_TRANSACTION); + res = SPI_OK_UTILITY; + if (plan == NULL) + { + ProcessUtility(queryTree->utilityStmt, None); + if (i < qlen - 1) + CommandCounterIncrement(); + else + return (res); + } + else if (i >= qlen - 1) + break; + } + else if (plan == NULL) + { + qdesc = CreateQueryDesc(queryTree, planTree, + (i < qlen - 1) ? None : SPI); + state = CreateExecutorState(); + res = _SPI_pquery(qdesc, state, (i < qlen - 1) ? 0 : tcount); + if (res < 0 || i >= qlen - 1) + return (res); + CommandCounterIncrement(); + } + else + { + qdesc = CreateQueryDesc(queryTree, planTree, + (i < qlen - 1) ? None : SPI); + res = _SPI_pquery(qdesc, NULL, (i < qlen - 1) ? 0 : tcount); + if (res < 0) + return (res); + if (i >= qlen - 1) + break; + } + } + + plan->qtlist = queryTree_list; + plan->ptlist = ptlist; + + return (res); + } static int -_SPI_execute_plan (_SPI_plan *plan, char **Values, char *Nulls, int tcount) +_SPI_execute_plan(_SPI_plan * plan, char **Values, char *Nulls, int tcount) { - QueryTreeList *queryTree_list = plan->qtlist; - List *planTree_list = plan->ptlist; - QueryDesc *qdesc; - Query *queryTree; - Plan *planTree; - EState *state; - int nargs = plan->nargs; - int qlen = queryTree_list->len; - int res; - int i, k; - - /* Increment CommandCounter to see changes made by now */ - CommandCounterIncrement (); - - SPI_processed = 0; - SPI_tuptable = NULL; - _SPI_current->tuptable = NULL; - _SPI_current->qtlist = NULL; - - for (i=0; ;i++) - { - queryTree = (Query*) (queryTree_list->qtrees[i]); - planTree = lfirst(planTree_list); - - planTree_list = lnext (planTree_list); - - if ( queryTree->commandType == CMD_UTILITY ) - { - ProcessUtility (queryTree->utilityStmt, None); - if ( i < qlen - 1 ) - CommandCounterIncrement (); - else - return (SPI_OK_UTILITY); - } - else + QueryTreeList *queryTree_list = plan->qtlist; + List *planTree_list = plan->ptlist; + QueryDesc *qdesc; + Query *queryTree; + Plan *planTree; + EState *state; + int nargs = plan->nargs; + int qlen = queryTree_list->len; + int res; + int i, + k; + + /* Increment CommandCounter to see changes made by now */ + CommandCounterIncrement(); + + SPI_processed = 0; + SPI_tuptable = NULL; + _SPI_current->tuptable = NULL; + _SPI_current->qtlist = NULL; + + for (i = 0;; i++) { - qdesc = CreateQueryDesc (queryTree, planTree, - ( i < qlen - 1 ) ? None : SPI); - state = CreateExecutorState(); - if ( nargs > 0 ) - { - ParamListInfo paramLI = (ParamListInfo) palloc ((nargs + 1) * - sizeof (ParamListInfoData)); - state->es_param_list_info = paramLI; - for (k = 0; k < plan->nargs; paramLI++, k++) - { - paramLI->kind = PARAM_NUM; - paramLI->id = k+1; - paramLI->isnull = (Nulls[k] != 0); - paramLI->value = (Datum) Values[k]; - } - paramLI->kind = PARAM_INVALID; - } - else - state->es_param_list_info = NULL; - res = _SPI_pquery (qdesc, state, ( i < qlen - 1 ) ? 0 : tcount); - if ( res < 0 || i >= qlen - 1 ) - return (res); - CommandCounterIncrement (); - } - } - - return (res); - + queryTree = (Query *) (queryTree_list->qtrees[i]); + planTree = lfirst(planTree_list); + + planTree_list = lnext(planTree_list); + + if (queryTree->commandType == CMD_UTILITY) + { + ProcessUtility(queryTree->utilityStmt, None); + if (i < qlen - 1) + CommandCounterIncrement(); + else + return (SPI_OK_UTILITY); + } + else + { + qdesc = CreateQueryDesc(queryTree, planTree, + (i < qlen - 1) ? None : SPI); + state = CreateExecutorState(); + if (nargs > 0) + { + ParamListInfo paramLI = (ParamListInfo) palloc((nargs + 1) * + sizeof(ParamListInfoData)); + + state->es_param_list_info = paramLI; + for (k = 0; k < plan->nargs; paramLI++, k++) + { + paramLI->kind = PARAM_NUM; + paramLI->id = k + 1; + paramLI->isnull = (Nulls[k] != 0); + paramLI->value = (Datum) Values[k]; + } + paramLI->kind = PARAM_INVALID; + } + else + state->es_param_list_info = NULL; + res = _SPI_pquery(qdesc, state, (i < qlen - 1) ? 0 : tcount); + if (res < 0 || i >= qlen - 1) + return (res); + CommandCounterIncrement(); + } + } + + return (res); + } static int -_SPI_pquery (QueryDesc *queryDesc, EState *state, int tcount) +_SPI_pquery(QueryDesc * queryDesc, EState * state, int tcount) { - Query *parseTree; - Plan *plan; - int operation; - TupleDesc tupdesc; - bool isRetrieveIntoPortal = false; - bool isRetrieveIntoRelation = false; - char* intoName = NULL; - int res; - - parseTree = queryDesc->parsetree; - plan = queryDesc->plantree; - operation = queryDesc->operation; - - switch (operation) - { - case CMD_SELECT: - res = SPI_OK_SELECT; - if (parseTree->isPortal) - { - isRetrieveIntoPortal = true; - intoName = parseTree->into; - parseTree->isBinary = false; /* */ - - return (SPI_ERROR_CURSOR); - - } - else if (parseTree->into != NULL) /* select into table */ - { - res = SPI_OK_SELINTO; - isRetrieveIntoRelation = true; - } - break; - case CMD_INSERT: - res = SPI_OK_INSERT; - break; - case CMD_DELETE: - res = SPI_OK_DELETE; - break; - case CMD_UPDATE: - res = SPI_OK_UPDATE; - break; - default: - return (SPI_ERROR_OPUNKNOWN); - } - - if ( state == NULL ) /* plan preparation */ - return (res); + Query *parseTree; + Plan *plan; + int operation; + TupleDesc tupdesc; + bool isRetrieveIntoPortal = false; + bool isRetrieveIntoRelation = false; + char *intoName = NULL; + int res; + + parseTree = queryDesc->parsetree; + plan = queryDesc->plantree; + operation = queryDesc->operation; + + switch (operation) + { + case CMD_SELECT: + res = SPI_OK_SELECT; + if (parseTree->isPortal) + { + isRetrieveIntoPortal = true; + intoName = parseTree->into; + parseTree->isBinary = false; /* */ + + return (SPI_ERROR_CURSOR); + + } + else if (parseTree->into != NULL) /* select into table */ + { + res = SPI_OK_SELINTO; + isRetrieveIntoRelation = true; + } + break; + case CMD_INSERT: + res = SPI_OK_INSERT; + break; + case CMD_DELETE: + res = SPI_OK_DELETE; + break; + case CMD_UPDATE: + res = SPI_OK_UPDATE; + break; + default: + return (SPI_ERROR_OPUNKNOWN); + } + + if (state == NULL) /* plan preparation */ + return (res); #ifdef SPI_EXECUTOR_STATS - if ( ShowExecutorStats ) - ResetUsage (); + if (ShowExecutorStats) + ResetUsage(); #endif - tupdesc = ExecutorStart (queryDesc, state); - - /* Don't work currently */ - if (isRetrieveIntoPortal) - { - ProcessPortal(intoName, - parseTree, - plan, - state, - tupdesc, - None); - return (SPI_OK_CURSOR); - } - - ExecutorRun (queryDesc, state, EXEC_RUN, tcount); - - _SPI_current->processed = state->es_processed; - if ( operation == CMD_SELECT && queryDesc->dest == SPI ) - { - if ( _SPI_checktuples (isRetrieveIntoRelation) ) - elog (FATAL, "SPI_select: # of processed tuples check failed"); - } - - ExecutorEnd (queryDesc, state); - + tupdesc = ExecutorStart(queryDesc, state); + + /* Don't work currently */ + if (isRetrieveIntoPortal) + { + ProcessPortal(intoName, + parseTree, + plan, + state, + tupdesc, + None); + return (SPI_OK_CURSOR); + } + + ExecutorRun(queryDesc, state, EXEC_RUN, tcount); + + _SPI_current->processed = state->es_processed; + if (operation == CMD_SELECT && queryDesc->dest == SPI) + { + if (_SPI_checktuples(isRetrieveIntoRelation)) + elog(FATAL, "SPI_select: # of processed tuples check failed"); + } + + ExecutorEnd(queryDesc, state); + #ifdef SPI_EXECUTOR_STATS - if ( ShowExecutorStats ) - { - fprintf (stderr, "! Executor Stats:\n"); - ShowUsage (); - } + if (ShowExecutorStats) + { + fprintf(stderr, "! Executor Stats:\n"); + ShowUsage(); + } #endif - - if ( queryDesc->dest == SPI ) - { - SPI_processed = _SPI_current->processed; - SPI_tuptable = _SPI_current->tuptable; - } - - return (res); + + if (queryDesc->dest == SPI) + { + SPI_processed = _SPI_current->processed; + SPI_tuptable = _SPI_current->tuptable; + } + + return (res); } #if 0 static void -_SPI_fetch (FetchStmt *stmt) +_SPI_fetch(FetchStmt * stmt) { - char *name = stmt->portalname; - int feature = ( stmt->direction == FORWARD ) ? EXEC_FOR : EXEC_BACK; - int count = stmt->howMany; - Portal portal; - QueryDesc *queryDesc; - EState *state; - MemoryContext context; - - if ( name == NULL) - elog (FATAL, "SPI_fetch from blank portal unsupported"); - - portal = GetPortalByName (name); - if ( !PortalIsValid (portal) ) - elog (FATAL, "SPI_fetch: portal \"%s\" not found", name); - - context = MemoryContextSwitchTo((MemoryContext)PortalGetHeapMemory(portal)); - - queryDesc = PortalGetQueryDesc(portal); - state = PortalGetState(portal); - - ExecutorRun(queryDesc, state, feature, count); - - MemoryContextSwitchTo (context); /* switch to the normal Executor context */ - - _SPI_current->processed = state->es_processed; - if ( _SPI_checktuples (false) ) - elog (FATAL, "SPI_fetch: # of processed tuples check failed"); - - SPI_processed = _SPI_current->processed; - SPI_tuptable = _SPI_current->tuptable; - + char *name = stmt->portalname; + int feature = (stmt->direction == FORWARD) ? EXEC_FOR : EXEC_BACK; + int count = stmt->howMany; + Portal portal; + QueryDesc *queryDesc; + EState *state; + MemoryContext context; + + if (name == NULL) + elog(FATAL, "SPI_fetch from blank portal unsupported"); + + portal = GetPortalByName(name); + if (!PortalIsValid(portal)) + elog(FATAL, "SPI_fetch: portal \"%s\" not found", name); + + context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal)); + + queryDesc = PortalGetQueryDesc(portal); + state = PortalGetState(portal); + + ExecutorRun(queryDesc, state, feature, count); + + MemoryContextSwitchTo(context); /* switch to the normal Executor + * context */ + + _SPI_current->processed = state->es_processed; + if (_SPI_checktuples(false)) + elog(FATAL, "SPI_fetch: # of processed tuples check failed"); + + SPI_processed = _SPI_current->processed; + SPI_tuptable = _SPI_current->tuptable; + } + #endif -static MemoryContext -_SPI_execmem () +static MemoryContext +_SPI_execmem() { - MemoryContext oldcxt; - PortalHeapMemory phmem; - - phmem = PortalGetHeapMemory (_SPI_current->portal); - oldcxt = MemoryContextSwitchTo ((MemoryContext)phmem); - - return (oldcxt); - + MemoryContext oldcxt; + PortalHeapMemory phmem; + + phmem = PortalGetHeapMemory(_SPI_current->portal); + oldcxt = MemoryContextSwitchTo((MemoryContext) phmem); + + return (oldcxt); + } -static MemoryContext -_SPI_procmem () +static MemoryContext +_SPI_procmem() { - MemoryContext oldcxt; - PortalVariableMemory pvmem; - - pvmem = PortalGetVariableMemory (_SPI_current->portal); - oldcxt = MemoryContextSwitchTo ((MemoryContext)pvmem); - - return (oldcxt); - + MemoryContext oldcxt; + PortalVariableMemory pvmem; + + pvmem = PortalGetVariableMemory(_SPI_current->portal); + oldcxt = MemoryContextSwitchTo((MemoryContext) pvmem); + + return (oldcxt); + } /* @@ -781,108 +792,110 @@ _SPI_procmem () * */ static int -_SPI_begin_call (bool execmem) +_SPI_begin_call(bool execmem) { - if ( _SPI_curid + 1 != _SPI_connected ) - return (SPI_ERROR_UNCONNECTED); - _SPI_curid++; - if ( _SPI_current != &(_SPI_stack[_SPI_curid]) ) - elog (FATAL, "SPI: stack corrupted"); - - if ( execmem ) /* switch to the Executor memory context */ - { - _SPI_execmem (); - StartPortalAllocMode (DefaultAllocMode, 0); - } - - return (0); + if (_SPI_curid + 1 != _SPI_connected) + return (SPI_ERROR_UNCONNECTED); + _SPI_curid++; + if (_SPI_current != &(_SPI_stack[_SPI_curid])) + elog(FATAL, "SPI: stack corrupted"); + + if (execmem) /* switch to the Executor memory context */ + { + _SPI_execmem(); + StartPortalAllocMode(DefaultAllocMode, 0); + } + + return (0); } static int -_SPI_end_call (bool procmem) +_SPI_end_call(bool procmem) { - /* - * We' returning to procedure where _SPI_curid == _SPI_connected - 1 - */ - _SPI_curid--; - - if ( _SPI_current->qtlist) /* free _SPI_plan allocations */ - { - free (_SPI_current->qtlist->qtrees); - free (_SPI_current->qtlist); - _SPI_current->qtlist = NULL; - } - - if ( procmem ) /* switch to the procedure memory context */ - { /* but free Executor memory before */ - EndPortalAllocMode (); - _SPI_procmem (); - } - - return (0); + + /* + * We' returning to procedure where _SPI_curid == _SPI_connected - 1 + */ + _SPI_curid--; + + if (_SPI_current->qtlist) /* free _SPI_plan allocations */ + { + free(_SPI_current->qtlist->qtrees); + free(_SPI_current->qtlist); + _SPI_current->qtlist = NULL; + } + + if (procmem) /* switch to the procedure memory context */ + { /* but free Executor memory before */ + EndPortalAllocMode(); + _SPI_procmem(); + } + + return (0); } -static bool -_SPI_checktuples (bool isRetrieveIntoRelation) +static bool +_SPI_checktuples(bool isRetrieveIntoRelation) { - uint32 processed = _SPI_current->processed; - SPITupleTable *tuptable = _SPI_current->tuptable; - bool failed = false; - - if ( processed == 0 ) - { - if ( tuptable != NULL ) - failed = true; - } - else /* some tuples were processed */ - { - if ( tuptable == NULL ) /* spi_printtup was not called */ - { - if ( !isRetrieveIntoRelation ) - failed = true; - } - else if ( isRetrieveIntoRelation ) - failed = true; - else if ( processed != ( tuptable->alloced - tuptable->free ) ) - failed = true; - } - - return (failed); + uint32 processed = _SPI_current->processed; + SPITupleTable *tuptable = _SPI_current->tuptable; + bool failed = false; + + if (processed == 0) + { + if (tuptable != NULL) + failed = true; + } + else +/* some tuples were processed */ + { + if (tuptable == NULL) /* spi_printtup was not called */ + { + if (!isRetrieveIntoRelation) + failed = true; + } + else if (isRetrieveIntoRelation) + failed = true; + else if (processed != (tuptable->alloced - tuptable->free)) + failed = true; + } + + return (failed); } - + static _SPI_plan * -_SPI_copy_plan (_SPI_plan *plan, bool local) +_SPI_copy_plan(_SPI_plan * plan, bool local) { - _SPI_plan *newplan; - MemoryContext oldcxt; - int i; - - if ( local ) - oldcxt = MemoryContextSwitchTo ((MemoryContext) - PortalGetVariableMemory (_SPI_current->portal)); - else - oldcxt = MemoryContextSwitchTo (TopMemoryContext); - - newplan = (_SPI_plan *) palloc (sizeof (_SPI_plan)); - newplan->qtlist = (QueryTreeList*) palloc (sizeof (QueryTreeList)); - newplan->qtlist->len = plan->qtlist->len; - newplan->qtlist->qtrees = (Query**) palloc (plan->qtlist->len * - sizeof (Query*)); - for (i = 0; i < plan->qtlist->len; i++) - newplan->qtlist->qtrees[i] = (Query *) - copyObject (plan->qtlist->qtrees[i]); - - newplan->ptlist = (List *) copyObject (plan->ptlist); - newplan->nargs = plan->nargs; - if ( plan->nargs > 0 ) - { - newplan->argtypes = (Oid *) palloc (plan->nargs * sizeof (Oid)); - memcpy (newplan->argtypes, plan->argtypes, plan->nargs * sizeof (Oid)); - } - else - newplan->argtypes = NULL; - - MemoryContextSwitchTo (oldcxt); - - return (newplan); + _SPI_plan *newplan; + MemoryContext oldcxt; + int i; + + if (local) + oldcxt = MemoryContextSwitchTo((MemoryContext) + PortalGetVariableMemory(_SPI_current->portal)); + else + oldcxt = MemoryContextSwitchTo(TopMemoryContext); + + newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan)); + newplan->qtlist = (QueryTreeList *) palloc(sizeof(QueryTreeList)); + newplan->qtlist->len = plan->qtlist->len; + newplan->qtlist->qtrees = (Query **) palloc(plan->qtlist->len * + sizeof(Query *)); + for (i = 0; i < plan->qtlist->len; i++) + newplan->qtlist->qtrees[i] = (Query *) + copyObject(plan->qtlist->qtrees[i]); + + newplan->ptlist = (List *) copyObject(plan->ptlist); + newplan->nargs = plan->nargs; + if (plan->nargs > 0) + { + newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid)); + memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid)); + } + else + newplan->argtypes = NULL; + + MemoryContextSwitchTo(oldcxt); + + return (newplan); } |