📄 execmain.c
字号:
/*------------------------------------------------------------------------- * * execMain.c * top level executor interface routines * * INTERFACE ROUTINES * ExecutorStart() * ExecutorRun() * 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 is required as an argument. * * ExecutorStart() must be called at the beginning of execution of any * query plan and ExecutorEnd() should always be called at the end of * execution of a plan. * * ExecutorRun accepts direction and count arguments that specify whether * the plan is to be executed forwards, backwards, and for how many tuples. * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.220.2.2 2004/03/02 18:56:28 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "catalog/heap.h"#include "catalog/namespace.h"#include "commands/tablecmds.h"#include "commands/trigger.h"#include "executor/execdebug.h"#include "executor/execdefs.h"#include "miscadmin.h"#include "optimizer/var.h"#include "parser/parsetree.h"#include "utils/acl.h"#include "utils/lsyscache.h"typedef struct execRowMark{ Relation relation; Index rti; char resname[32];} execRowMark;typedef struct evalPlanQual{ Index rti; EState *estate; PlanState *planstate; struct evalPlanQual *next; /* stack of active PlanQual plans */ struct evalPlanQual *free; /* list of free PlanQual plans */} evalPlanQual;/* decls for local routines only used within this module */static void InitPlan(QueryDesc *queryDesc, bool explainOnly);static void initResultRelInfo(ResultRelInfo *resultRelInfo, Index resultRelationIndex, List *rangeTable, CmdType operation);static TupleTableSlot *ExecutePlan(EState *estate, PlanState *planstate, CmdType operation, long numberTuples, ScanDirection direction, DestReceiver *dest);static void ExecSelect(TupleTableSlot *slot, DestReceiver *dest, EState *estate);static void ExecInsert(TupleTableSlot *slot, ItemPointer tupleid, EState *estate);static void ExecDelete(TupleTableSlot *slot, ItemPointer tupleid, EState *estate);static void ExecUpdate(TupleTableSlot *slot, ItemPointer tupleid, EState *estate);static TupleTableSlot *EvalPlanQualNext(EState *estate);static void EndEvalPlanQual(EState *estate);static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation);static void ExecCheckXactReadOnly(Query *parsetree, CmdType operation);static void EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq);static void EvalPlanQualStop(evalPlanQual *epq);/* end of local decls *//* ---------------------------------------------------------------- * ExecutorStart * * This routine must be called at the beginning of any execution of any * query plan * * Takes a QueryDesc previously created by CreateQueryDesc (it's not real * clear why we bother to separate the two functions, but...). The tupDesc * field of the QueryDesc is filled in to describe the tuples that will be * returned, and the internal fields (estate and planstate) are set up. * * If useCurrentSnapshot is true, run the query with the latest available * snapshot, instead of the normal QuerySnapshot. Also, if it's an update * or delete query, check that the rows to be updated or deleted would be * visible to the normal QuerySnapshot. (This is a special-case behavior * needed for referential integrity updates in serializable transactions. * We must check all currently-committed rows, but we want to throw a * can't-serialize error if any rows that would need updates would not be * visible under the normal serializable snapshot.) * * If explainOnly is true, we are not actually intending to run the plan, * only to set up for EXPLAIN; so skip unwanted side-effects. * * NB: the CurrentMemoryContext when this is called will become the parent * of the per-query context used for this Executor invocation. * ---------------------------------------------------------------- */voidExecutorStart(QueryDesc *queryDesc, bool useCurrentSnapshot, bool explainOnly){ EState *estate; MemoryContext oldcontext; /* sanity checks: queryDesc must not be started already */ Assert(queryDesc != NULL); Assert(queryDesc->estate == NULL); /* * If the transaction is read-only, we need to check if any writes are * planned to non-temporary tables. */ if (!explainOnly) ExecCheckXactReadOnly(queryDesc->parsetree, queryDesc->operation); /* * Build EState, switch into per-query memory context for startup. */ estate = CreateExecutorState(); queryDesc->estate = estate; oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); /* * Fill in parameters, if any, from queryDesc */ estate->es_param_list_info = queryDesc->params; if (queryDesc->plantree->nParamExec > 0) estate->es_param_exec_vals = (ParamExecData *) palloc0(queryDesc->plantree->nParamExec * sizeof(ParamExecData)); estate->es_instrument = queryDesc->doInstrument; /* * Make our own private copy of the current query snapshot data. * * This "freezes" our idea of which tuples are good and which are not for * the life of this query, even if it outlives the current command and * current snapshot. */ if (useCurrentSnapshot) { /* RI update/delete query --- must use an up-to-date snapshot */ estate->es_snapshot = CopyCurrentSnapshot(); /* crosscheck updates/deletes against transaction snapshot */ estate->es_crosscheck_snapshot = CopyQuerySnapshot(); } else { /* normal query --- use query snapshot, no crosscheck */ estate->es_snapshot = CopyQuerySnapshot(); estate->es_crosscheck_snapshot = SnapshotAny; } /* * Initialize the plan state tree */ InitPlan(queryDesc, explainOnly); MemoryContextSwitchTo(oldcontext);}/* ---------------------------------------------------------------- * 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. * * If direction is NoMovementScanDirection then nothing is done * except to start up/shut down the destination. Otherwise, * we retrieve up to 'count' tuples in the specified direction. * * Note: count = 0 is interpreted as no portal limit, i.e., run to * completion. * * ---------------------------------------------------------------- */TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count){ EState *estate; CmdType operation; DestReceiver *dest; TupleTableSlot *result; MemoryContext oldcontext; /* sanity checks */ Assert(queryDesc != NULL); estate = queryDesc->estate; Assert(estate != NULL); /* * Switch into per-query memory context */ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); /* * extract information from the query descriptor and the query * feature. */ operation = queryDesc->operation; dest = queryDesc->dest; /* * startup tuple receiver */ estate->es_processed = 0; estate->es_lastoid = InvalidOid; (*dest->rStartup) (dest, operation, queryDesc->tupDesc); /* * run plan */ if (direction == NoMovementScanDirection) result = NULL; else result = ExecutePlan(estate, queryDesc->planstate, operation, count, direction, dest); /* * shutdown receiver */ (*dest->rShutdown) (dest); MemoryContextSwitchTo(oldcontext); return result;}/* ---------------------------------------------------------------- * ExecutorEnd * * This routine must be called at the end of execution of any * query plan * ---------------------------------------------------------------- */voidExecutorEnd(QueryDesc *queryDesc){ EState *estate; MemoryContext oldcontext; /* sanity checks */ Assert(queryDesc != NULL); estate = queryDesc->estate; Assert(estate != NULL); /* * Switch into per-query memory context to run ExecEndPlan */ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); ExecEndPlan(queryDesc->planstate, estate); /* * Must switch out of context before destroying it */ MemoryContextSwitchTo(oldcontext); /* * Release EState and per-query memory context. This should release * everything the executor has allocated. */ FreeExecutorState(estate); /* Reset queryDesc fields that no longer point to anything */ queryDesc->tupDesc = NULL; queryDesc->estate = NULL; queryDesc->planstate = NULL;}/* ---------------------------------------------------------------- * ExecutorRewind * * This routine may be called on an open queryDesc to rewind it * to the start. * ---------------------------------------------------------------- */voidExecutorRewind(QueryDesc *queryDesc){ EState *estate; MemoryContext oldcontext; /* sanity checks */ Assert(queryDesc != NULL); estate = queryDesc->estate; Assert(estate != NULL); /* It's probably not sensible to rescan updating queries */ Assert(queryDesc->operation == CMD_SELECT); /* * Switch into per-query memory context */ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); /* * rescan plan */ ExecReScan(queryDesc->planstate, NULL); MemoryContextSwitchTo(oldcontext);}/* * ExecCheckRTPerms * Check access permissions for all relations listed in a range table. */voidExecCheckRTPerms(List *rangeTable, CmdType operation){ List *lp; foreach(lp, rangeTable) { RangeTblEntry *rte = lfirst(lp); ExecCheckRTEPerms(rte, operation); }}/* * ExecCheckRTEPerms * Check access permissions for a single RTE. */static voidExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation){ Oid relOid; AclId userid; AclResult aclcheck_result; /* * If it's a subquery, recursively examine its rangetable. */ if (rte->rtekind == RTE_SUBQUERY) { ExecCheckRTPerms(rte->subquery->rtable, operation); return; } /* * Otherwise, only plain-relation RTEs need to be checked here. * Function RTEs are checked by init_fcache when the function is * prepared for execution. Join and special RTEs need no checks. */ if (rte->rtekind != RTE_RELATION) return; relOid = rte->relid; /* * userid to check as: current user unless we have a setuid * indication. * * Note: GetUserId() is presently fast enough that there's no harm in * calling it separately for each RTE. If that stops being true, we * could call it once in ExecCheckRTPerms and pass the userid down * from there. But for now, no need for the extra clutter. */ userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();#define CHECK(MODE) pg_class_aclcheck(relOid, userid, MODE) if (rte->checkForRead) { aclcheck_result = CHECK(ACL_SELECT); if (aclcheck_result != ACLCHECK_OK) aclcheck_error(aclcheck_result, ACL_KIND_CLASS, get_rel_name(relOid)); } if (rte->checkForWrite) { /* * Note: write access in a SELECT context means SELECT FOR UPDATE. * Right now we don't distinguish that from true update as far as * permissions checks are concerned. */ switch (operation) { case CMD_INSERT: aclcheck_result = CHECK(ACL_INSERT); break; case CMD_SELECT: case CMD_UPDATE: aclcheck_result = CHECK(ACL_UPDATE); break; case CMD_DELETE: aclcheck_result = CHECK(ACL_DELETE); break; default: elog(ERROR, "unrecognized operation code: %d", (int) operation); aclcheck_result = ACLCHECK_OK; /* keep compiler quiet */ break; } if (aclcheck_result != ACLCHECK_OK) aclcheck_error(aclcheck_result, ACL_KIND_CLASS, get_rel_name(relOid)); }}static voidExecCheckXactReadOnly(Query *parsetree, CmdType operation){ if (!XactReadOnly) return; /* CREATE TABLE AS or SELECT INTO */ if (operation == CMD_SELECT && parsetree->into != NULL) goto fail; if (operation == CMD_DELETE || operation == CMD_INSERT || operation == CMD_UPDATE) { List *lp; foreach(lp, parsetree->rtable) { RangeTblEntry *rte = lfirst(lp); if (rte->rtekind != RTE_RELATION) continue; if (!rte->checkForWrite) continue; if (isTempNamespace(get_rel_namespace(rte->relid))) continue; goto fail; } } return;fail: ereport(ERROR, (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), errmsg("transaction is read-only")));}/* ---------------------------------------------------------------- * InitPlan * * Initializes the query plan: open files, allocate storage * and start up the rule manager * ---------------------------------------------------------------- */static voidInitPlan(QueryDesc *queryDesc, bool explainOnly){ CmdType operation = queryDesc->operation; Query *parseTree = queryDesc->parsetree; Plan *plan = queryDesc->plantree; EState *estate = queryDesc->estate; PlanState *planstate; List *rangeTable; Relation intoRelationDesc; bool do_select_into; TupleDesc tupType; /* * Do permissions checks. It's sufficient to examine the query's top * rangetable here --- subplan RTEs will be checked during * ExecInitSubPlan(). */ ExecCheckRTPerms(parseTree->rtable, operation); /* * get information from query descriptor */ rangeTable = parseTree->rtable; /* * initialize the node's execution state */ estate->es_range_table = rangeTable; /* * if there is a result relation, initialize result relation stuff */ if (parseTree->resultRelation != 0 && operation != CMD_SELECT) { List *resultRelations = parseTree->resultRelations; int numResultRelations; ResultRelInfo *resultRelInfos; if (resultRelations != NIL) { /* * Multiple result relations (due to inheritance) * parseTree->resultRelations identifies them all */ ResultRelInfo *resultRelInfo; numResultRelations = length(resultRelations); resultRelInfos = (ResultRelInfo *) palloc(numResultRelations * sizeof(ResultRelInfo)); resultRelInfo = resultRelInfos;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -