diff options
Diffstat (limited to 'src/backend/executor/execMain.c')
-rw-r--r-- | src/backend/executor/execMain.c | 2324 |
1 files changed, 1189 insertions, 1135 deletions
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); } |