📄 execmain.c
字号:
while (resultRelations != NIL) { initResultRelInfo(resultRelInfo, lfirsti(resultRelations), rangeTable, operation); resultRelInfo++; resultRelations = lnext(resultRelations); } } else { /* * Single result relation identified by * parseTree->resultRelation */ numResultRelations = 1; resultRelInfos = (ResultRelInfo *) palloc(sizeof(ResultRelInfo)); initResultRelInfo(resultRelInfos, parseTree->resultRelation, rangeTable, operation); } estate->es_result_relations = resultRelInfos; estate->es_num_result_relations = numResultRelations; /* Initialize to first or only result rel */ estate->es_result_relation_info = resultRelInfos; } else { /* * if no result relation, then set state appropriately */ estate->es_result_relations = NULL; estate->es_num_result_relations = 0; estate->es_result_relation_info = NULL; } /* * Detect whether we're doing SELECT INTO. If so, set the force_oids * flag appropriately so that the plan tree will be initialized with * the correct tuple descriptors. */ do_select_into = false; if (operation == CMD_SELECT && parseTree->into != NULL) { do_select_into = true; estate->es_select_into = true; /* * For now, always create OIDs in SELECT INTO; this is for * backwards compatibility with pre-7.3 behavior. Eventually we * might want to allow the user to choose. */ estate->es_into_oids = true; } /* * Have to lock relations selected for update */ estate->es_rowMark = NIL; if (parseTree->rowMarks != NIL) { List *l; foreach(l, parseTree->rowMarks) { Index rti = lfirsti(l); Oid relid = getrelid(rti, rangeTable); Relation relation; execRowMark *erm; relation = heap_open(relid, RowShareLock); erm = (execRowMark *) palloc(sizeof(execRowMark)); erm->relation = relation; erm->rti = rti; snprintf(erm->resname, sizeof(erm->resname), "ctid%u", rti); estate->es_rowMark = lappend(estate->es_rowMark, erm); } } /* * initialize the executor "tuple" table. We need slots for all the * plan nodes, plus possibly output slots for the junkfilter(s). At * this point we aren't sure if we need junkfilters, so just add slots * for them unconditionally. */ { int nSlots = ExecCountSlotsNode(plan); if (parseTree->resultRelations != NIL) nSlots += length(parseTree->resultRelations); else nSlots += 1; estate->es_tupleTable = ExecCreateTupleTable(nSlots); } /* mark EvalPlanQual not active */ estate->es_topPlan = plan; estate->es_evalPlanQual = NULL; estate->es_evTupleNull = NULL; estate->es_evTuple = NULL; estate->es_useEvalPlan = false; /* * 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. */ planstate = ExecInitNode(plan, estate); /* * Get the tuple descriptor describing the type of tuples to return. * (this is especially important if we are creating a relation with * "SELECT INTO") */ tupType = ExecGetResultType(planstate); /* * Initialize the junk filter if needed. SELECT and INSERT queries * need a filter if there are any junk attrs in the tlist. INSERT and * SELECT INTO also need a filter if the plan may return raw disk tuples * (else heap_insert will be scribbling on the source relation!). * UPDATE and DELETE always need a filter, since there's always a junk * 'ctid' attribute present --- no need to look first. */ { bool junk_filter_needed = false; List *tlist; switch (operation) { case CMD_SELECT: case CMD_INSERT: foreach(tlist, plan->targetlist) { TargetEntry *tle = (TargetEntry *) lfirst(tlist); if (tle->resdom->resjunk) { junk_filter_needed = true; break; } } if (!junk_filter_needed && (operation == CMD_INSERT || do_select_into) && ExecMayReturnRawTuples(planstate)) junk_filter_needed = true; break; case CMD_UPDATE: case CMD_DELETE: junk_filter_needed = true; break; default: break; } if (junk_filter_needed) { /* * If there are multiple result relations, each one needs its * own junk filter. Note this is only possible for * UPDATE/DELETE, so we can't be fooled by some needing a * filter and some not. */ if (parseTree->resultRelations != NIL) { PlanState **appendplans; int as_nplans; ResultRelInfo *resultRelInfo; int i; /* Top plan had better be an Append here. */ Assert(IsA(plan, Append)); Assert(((Append *) plan)->isTarget); Assert(IsA(planstate, AppendState)); appendplans = ((AppendState *) planstate)->appendplans; as_nplans = ((AppendState *) planstate)->as_nplans; Assert(as_nplans == estate->es_num_result_relations); resultRelInfo = estate->es_result_relations; for (i = 0; i < as_nplans; i++) { PlanState *subplan = appendplans[i]; JunkFilter *j; j = ExecInitJunkFilter(subplan->plan->targetlist, ExecGetResultType(subplan), ExecAllocTableSlot(estate->es_tupleTable)); resultRelInfo->ri_junkFilter = j; resultRelInfo++; } /* * Set active junkfilter too; at this point ExecInitAppend * has already selected an active result relation... */ estate->es_junkFilter = estate->es_result_relation_info->ri_junkFilter; } else { /* Normal case with just one JunkFilter */ JunkFilter *j; j = ExecInitJunkFilter(planstate->plan->targetlist, tupType, ExecAllocTableSlot(estate->es_tupleTable)); estate->es_junkFilter = j; if (estate->es_result_relation_info) estate->es_result_relation_info->ri_junkFilter = j; /* For SELECT, want to return the cleaned tuple type */ if (operation == CMD_SELECT) tupType = j->jf_cleanTupType; } } else estate->es_junkFilter = NULL; } /* * If doing SELECT INTO, initialize the "into" relation. We must wait * till now so we have the "clean" result tuple type to create the new * table from. * * If EXPLAIN, skip creating the "into" relation. */ intoRelationDesc = (Relation) NULL; if (do_select_into && !explainOnly) { char *intoName; Oid namespaceId; AclResult aclresult; Oid intoRelationId; TupleDesc tupdesc; /* * find namespace to create in, check permissions */ intoName = parseTree->into->relname; namespaceId = RangeVarGetCreationNamespace(parseTree->into); aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceId)); /* * have to copy tupType to get rid of constraints */ tupdesc = CreateTupleDescCopy(tupType); intoRelationId = heap_create_with_catalog(intoName, namespaceId, tupdesc, RELKIND_RELATION, false, ONCOMMIT_NOOP, allowSystemTableMods); FreeTupleDesc(tupdesc); /* * Advance command counter so that the newly-created relation's * catalog tuples will be visible to heap_open. */ CommandCounterIncrement(); /* * If necessary, create a TOAST table for the into relation. Note * that AlterTableCreateToastTable ends with * CommandCounterIncrement(), so that the TOAST table will be * visible for insertion. */ AlterTableCreateToastTable(intoRelationId, true); /* * And open the constructed table for writing. */ intoRelationDesc = heap_open(intoRelationId, AccessExclusiveLock); } estate->es_into_relation_descriptor = intoRelationDesc; queryDesc->tupDesc = tupType; queryDesc->planstate = planstate;}/* * Initialize ResultRelInfo data for one result relation */static voidinitResultRelInfo(ResultRelInfo *resultRelInfo, Index resultRelationIndex, List *rangeTable, CmdType operation){ Oid resultRelationOid; Relation resultRelationDesc; resultRelationOid = getrelid(resultRelationIndex, rangeTable); resultRelationDesc = heap_open(resultRelationOid, RowExclusiveLock); switch (resultRelationDesc->rd_rel->relkind) { case RELKIND_SEQUENCE: ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot change sequence \"%s\"", RelationGetRelationName(resultRelationDesc)))); break; case RELKIND_TOASTVALUE: ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot change TOAST relation \"%s\"", RelationGetRelationName(resultRelationDesc)))); break; case RELKIND_VIEW: ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot change view \"%s\"", RelationGetRelationName(resultRelationDesc)))); break; } MemSet(resultRelInfo, 0, sizeof(ResultRelInfo)); resultRelInfo->type = T_ResultRelInfo; resultRelInfo->ri_RangeTableIndex = resultRelationIndex; resultRelInfo->ri_RelationDesc = resultRelationDesc; resultRelInfo->ri_NumIndices = 0; resultRelInfo->ri_IndexRelationDescs = NULL; resultRelInfo->ri_IndexRelationInfo = NULL; /* make a copy so as not to depend on relcache info not changing... */ resultRelInfo->ri_TrigDesc = CopyTriggerDesc(resultRelationDesc->trigdesc); resultRelInfo->ri_TrigFunctions = NULL; resultRelInfo->ri_ConstraintExprs = NULL; resultRelInfo->ri_junkFilter = NULL; /* * If there are indices on the result relation, open them and save * descriptors in the result relation info, so that we can add new * index entries for the tuples we add/update. We need not do this * for a DELETE, however, since deletion doesn't affect indexes. */ if (resultRelationDesc->rd_rel->relhasindex && operation != CMD_DELETE) ExecOpenIndices(resultRelInfo);}/* * ExecContextForcesOids * * This is pretty grotty: when doing INSERT, UPDATE, or SELECT INTO, * we need to ensure that result tuples have space for an OID iff they are * going to be stored into a relation that has OIDs. In other contexts * we are free to choose whether to leave space for OIDs in result tuples * (we generally don't want to, but we do if a physical-tlist optimization * is possible). This routine checks the plan context and returns TRUE if the * choice is forced, FALSE if the choice is not forced. In the TRUE case, * *hasoids is set to the required value. * * One reason this is ugly is that all plan nodes in the plan tree will emit * tuples with space for an OID, though we really only need the topmost node * to do so. However, node types like Sort don't project new tuples but just * return their inputs, and in those cases the requirement propagates down * to the input node. Eventually we might make this code smart enough to * recognize how far down the requirement really goes, but for now we just * make all plan nodes do the same thing if the top level forces the choice. * * We assume that estate->es_result_relation_info is already set up to * describe the target relation. Note that in an UPDATE that spans an * inheritance tree, some of the target relations may have OIDs and some not. * We have to make the decisions on a per-relation basis as we initialize * each of the child plans of the topmost Append plan. * * SELECT INTO is even uglier, because we don't have the INTO relation's * descriptor available when this code runs; we have to look aside at a * flag set by InitPlan(). */boolExecContextForcesOids(PlanState *planstate, bool *hasoids){ if (planstate->state->es_select_into) { *hasoids = planstate->state->es_into_oids; return true; } else { ResultRelInfo *ri = planstate->state->es_result_relation_info; if (ri != NULL) { Relation rel = ri->ri_RelationDesc; if (rel != NULL) { *hasoids = rel->rd_rel->relhasoids; return true; } } } return false;}/* ---------------------------------------------------------------- * ExecEndPlan * * Cleans up the query plan -- closes files and frees up storage * * NOTE: we are no longer very worried about freeing storage per se * in this code; FreeExecutorState should be guaranteed to release all * memory that needs to be released. What we are worried about doing * is closing relations and dropping buffer pins. Thus, for example, * tuple tables must be cleared or dropped to ensure pins are released. * ---------------------------------------------------------------- */voidExecEndPlan(PlanState *planstate, EState *estate){ ResultRelInfo *resultRelInfo; int i; List *l; /* * shut down any PlanQual processing we were doing */ if (estate->es_evalPlanQual != NULL) EndEvalPlanQual(estate); /* * shut down the node-type-specific query processing */ ExecEndNode(planstate); /* * destroy the executor "tuple" table. */ ExecDropTupleTable(estate->es_tupleTable, true); estate->es_tupleTable = NULL; /* * close the result relation(s) if any, but hold locks until xact * commit. */ resultRelInfo = estate->es_result_relations; for (i = estate->es_num_result_relations; i > 0; i--) { /* Close indices and then the relation itself */ ExecCloseIndices(resultRelInfo); heap_close(resultRelInfo->ri_RelationDesc, NoLock); resultRelInfo++; } /* * close the "into" relation if necessary, again keeping lock */ if (estate->es_into_relation_descriptor != NULL) heap_close(estate->es_into_relation_descriptor, NoLock); /* * close any relations selected FOR UPDATE, again keeping locks */ foreach(l, estate->es_rowMark) { execRowMark *erm = lfirst(l); heap_close(erm->relation, NoLock); }}/* ---------------------------------------------------------------- * ExecutePlan * * processes the query plan to retrieve 'numberTuples' tuples in the * direction specified. * * Retrieves all tuples if numberTuples is 0 * * result is either a slot containing the last tuple in the case * of a SELECT or NULL otherwise. * * Note: the ctid attribute is a 'junk' attribute that is removed before the * user can see it * ---------------------------------------------------------------- */static TupleTableSlot *ExecutePlan(EState *estate, PlanState *planstate, CmdType operation, long numberTuples, ScanDirection direction, DestReceiver *dest){ JunkFilter *junkfilter; TupleTableSlot *slot; ItemPointer tupleid = NULL; ItemPointerData tuple_ctid; long current_tuple_count; TupleTableSlot *result; /* * initialize local variables */ slot = NULL; current_tuple_count = 0; result = NULL; /* * Set the direction. */ estate->es_direction = direction; /* * Process BEFORE EACH STATEMENT triggers */ switch (operation) { case CMD_UPDATE: ExecBSUpdateTriggers(estate, estate->es_result_relation_info); break; case CMD_DELETE: ExecBSDeleteTriggers(estate, estate->es_result_relation_info); break; case CMD_INSERT: ExecBSInsertTriggers(estate, estate->es_result_relation_info); break; default: /* do nothing */ break; } /* * Loop until we've processed the proper number of tuples from the * plan. */ for (;;) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -