📄 execmain.c
字号:
* * 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; ListCell *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) { /* * If we skipped using WAL, and it's not a temp relation, we must * force the relation down to disk before it's safe to commit the * transaction. This requires forcing out any dirty buffers and then * doing a forced fsync. */ if (!estate->es_into_relation_use_wal && !estate->es_into_relation_descriptor->rd_istemp) { FlushRelationBuffers(estate->es_into_relation_descriptor); smgrimmedsync(estate->es_into_relation_descriptor->rd_smgr); } heap_close(estate->es_into_relation_descriptor, NoLock); } /* * close any relations selected FOR UPDATE/FOR SHARE, again keeping locks */ foreach(l, estate->es_rowMarks) { 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 (;;) { /* Reset the per-output-tuple exprcontext */ ResetPerTupleExprContext(estate); /* * Execute the plan and obtain a tuple */lnext: ; if (estate->es_useEvalPlan) { slot = EvalPlanQualNext(estate); if (TupIsNull(slot)) slot = ExecProcNode(planstate); } else slot = ExecProcNode(planstate); /* * 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 junkfilter's resultSlot. * (Formerly, we stored it back over the "dirty" tuple, which is WRONG * because that tuple slot has the wrong descriptor.) * * Also, extract all the junk information we need. */ if ((junkfilter = estate->es_junkFilter) != NULL) { Datum datum; bool isNull; /* * extract the 'ctid' junk attribute. */ if (operation == CMD_UPDATE || operation == CMD_DELETE) { if (!ExecGetJunkAttribute(junkfilter, slot, "ctid", &datum, &isNull)) elog(ERROR, "could not find junk ctid column"); /* shouldn't ever get a null result... */ if (isNull) elog(ERROR, "ctid is NULL"); tupleid = (ItemPointer) DatumGetPointer(datum); tuple_ctid = *tupleid; /* make sure we don't free the ctid!! */ tupleid = &tuple_ctid; } /* * Process any FOR UPDATE or FOR SHARE locking requested. */ else if (estate->es_rowMarks != NIL) { ListCell *l; lmark: ; foreach(l, estate->es_rowMarks) { execRowMark *erm = lfirst(l); HeapTupleData tuple; Buffer buffer; ItemPointerData update_ctid; TransactionId update_xmax; TupleTableSlot *newSlot; LockTupleMode lockmode; HTSU_Result test; if (!ExecGetJunkAttribute(junkfilter, slot, erm->resname, &datum, &isNull)) elog(ERROR, "could not find junk \"%s\" column", erm->resname); /* shouldn't ever get a null result... */ if (isNull) elog(ERROR, "\"%s\" is NULL", erm->resname); tuple.t_self = *((ItemPointer) DatumGetPointer(datum)); if (estate->es_forUpdate) lockmode = LockTupleExclusive; else lockmode = LockTupleShared; test = heap_lock_tuple(erm->relation, &tuple, &buffer, &update_ctid, &update_xmax, estate->es_snapshot->curcid, lockmode, estate->es_rowNoWait); ReleaseBuffer(buffer); switch (test) { case HeapTupleSelfUpdated: /* treat it as deleted; do not process */ goto lnext; case HeapTupleMayBeUpdated: break; case HeapTupleUpdated: if (IsXactIsoLevelSerializable) ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("could not serialize access due to concurrent update"))); if (!ItemPointerEquals(&update_ctid, &tuple.t_self)) { /* updated, so look at updated version */ newSlot = EvalPlanQual(estate, erm->rti, &update_ctid, update_xmax, estate->es_snapshot->curcid); if (!TupIsNull(newSlot)) { slot = newSlot; estate->es_useEvalPlan = true; goto lmark; } } /* * if tuple was deleted or PlanQual failed for * updated tuple - we must not return this tuple! */ goto lnext; default: elog(ERROR, "unrecognized heap_lock_tuple status: %u", test); return (NULL); } } } /* * Finally create a new "clean" tuple with all junk attributes * removed */ slot = ExecFilterJunk(junkfilter, slot); } /* * 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 its attributes. */ switch (operation) { case CMD_SELECT: ExecSelect(slot, /* slot containing tuple */ dest, /* destination's tuple-receiver obj */ estate); result = slot; break; case CMD_INSERT: ExecInsert(slot, tupleid, estate); result = NULL; break; case CMD_DELETE: ExecDelete(slot, tupleid, estate); result = NULL; break; case CMD_UPDATE: ExecUpdate(slot, tupleid, estate); result = NULL; break; default: elog(ERROR, "unrecognized operation code: %d", (int) operation); result = NULL; break; } /* * check our tuple count.. if we've processed the proper number then * quit, else loop again and process more tuples. Zero numberTuples * means no limit. */ current_tuple_count++; if (numberTuples && numberTuples == current_tuple_count) break; } /* * Process AFTER EACH STATEMENT triggers */ switch (operation) { case CMD_UPDATE: ExecASUpdateTriggers(estate, estate->es_result_relation_info); break; case CMD_DELETE: ExecASDeleteTriggers(estate, estate->es_result_relation_info); break; case CMD_INSERT: ExecASInsertTriggers(estate, estate->es_result_relation_info); break; default: /* do nothing */ break; } /* * here, result is either a slot containing a tuple in the case of a * SELECT or NULL otherwise. */ return result;}/* ---------------------------------------------------------------- * ExecSelect * * SELECTs are easy.. we just pass the tuple to the appropriate * print function. The only complexity is when we do a * "SELECT 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 voidExecSelect(TupleTableSlot *slot, DestReceiver *dest, EState *estate){ /* * insert the tuple into the "into relation" * * XXX this probably ought to be replaced by a separate destination */ if (estate->es_into_relation_descriptor != NULL) { HeapTuple tuple; tuple = ExecCopySlotTuple(slot); heap_insert(estate->es_into_relation_descriptor, tuple, estate->es_snapshot->curcid, estate->es_into_relation_use_wal, false); /* never any point in using FSM */ /* we know there are no indexes to update */ heap_freetuple(tuple); IncrAppended(); } /* * send the tuple to the destination */ (*dest->receiveSlot) (slot, dest); IncrRetrieved(); (estate->es_processed)++;}/* ---------------------------------------------------------------- * ExecInsert * * INSERTs are trickier.. we have to insert the tuple into * the base relation and insert appropriate tuples into the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -