📄 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-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.256.2.6 2006/04/26 23:01:58 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "access/xlog.h"#include "catalog/heap.h"#include "catalog/namespace.h"#include "commands/tablecmds.h"#include "commands/tablespace.h"#include "commands/trigger.h"#include "executor/execdebug.h"#include "executor/execdefs.h"#include "executor/instrument.h"#include "miscadmin.h"#include "optimizer/clauses.h"#include "optimizer/var.h"#include "parser/parsetree.h"#include "storage/smgr.h"#include "utils/acl.h"#include "utils/guc.h"#include "utils/lsyscache.h"#include "utils/memutils.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, bool doInstrument);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);static void ExecCheckXactReadOnly(Query *parsetree);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 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 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 (XactReadOnly && !explainOnly) ExecCheckXactReadOnly(queryDesc->parsetree); /* * 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)); /* * Copy other important information into the EState */ estate->es_snapshot = queryDesc->snapshot; estate->es_crosscheck_snapshot = queryDesc->crosscheck_snapshot; estate->es_instrument = queryDesc->doInstrument; /* * 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){ ListCell *l; foreach(l, rangeTable) { RangeTblEntry *rte = lfirst(l); ExecCheckRTEPerms(rte); }}/* * ExecCheckRTEPerms * Check access permissions for a single RTE. */static voidExecCheckRTEPerms(RangeTblEntry *rte){ AclMode requiredPerms; Oid relOid; Oid userid; /* * Only plain-relation RTEs need to be checked here. Subquery RTEs are * checked by ExecInitSubqueryScan if the subquery is still a separate * subquery --- if it's been pulled up into our query level then the RTEs * are in our rangetable and will 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; /* * No work if requiredPerms is empty. */ requiredPerms = rte->requiredPerms; if (requiredPerms == 0) 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(); /* * We must have *all* the requiredPerms bits, so use aclmask not aclcheck. */ if (pg_class_aclmask(relOid, userid, requiredPerms, ACLMASK_ALL) != requiredPerms) aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, get_rel_name(relOid));}/* * Check that the query does not imply any writes to non-temp tables. */static voidExecCheckXactReadOnly(Query *parsetree){ ListCell *l; /* * CREATE TABLE AS or SELECT INTO? * * XXX should we allow this if the destination is temp? */ if (parsetree->into != NULL) goto fail; /* Fail if write permissions are requested on any non-temp table */ foreach(l, parsetree->rtable) { RangeTblEntry *rte = lfirst(l); if (rte->rtekind == RTE_SUBQUERY) { ExecCheckXactReadOnly(rte->subquery); continue; } if (rte->rtekind != RTE_RELATION) continue; if ((rte->requiredPerms & (~ACL_SELECT)) == 0) 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -