📄 execmain.c
字号:
{ TupleTableSlot *epqslot = EvalPlanQual(estate, resultRelInfo->ri_RangeTableIndex, &ctid); if (!TupIsNull(epqslot)) { *tupleid = ctid; tuple = ExecRemoveJunk(estate->es_junkFilter, epqslot); slot = ExecStoreTuple(tuple, estate->es_junkFilter->jf_resultSlot, InvalidBuffer, true); goto lreplace; } } /* tuple already deleted; nothing to do */ return; default: elog(ERROR, "unrecognized heap_update status: %u", result); 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 UPDATEs are actually DELETEs and INSERTs and index * tuple deletion is done automagically by the vacuum daemon. All we * do is insert new index tuples. -cim 9/27/89 */ /* * process indices * * heap_update updates a tuple in the base relation by invalidating it * and then inserting 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 = resultRelInfo->ri_NumIndices; if (numIndices > 0) ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); /* AFTER ROW UPDATE Triggers */ ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple);}static const char *ExecRelCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate){ Relation rel = resultRelInfo->ri_RelationDesc; int ncheck = rel->rd_att->constr->num_check; ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; List *qual; int i; /* * If first time through for this result relation, build expression * nodetrees for rel's constraint expressions. Keep them in the * per-query memory context so they'll survive throughout the query. */ if (resultRelInfo->ri_ConstraintExprs == NULL) { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = (List **) palloc(ncheck * sizeof(List *)); for (i = 0; i < ncheck; i++) { qual = (List *) stringToNode(check[i].ccbin); resultRelInfo->ri_ConstraintExprs[i] = (List *) ExecPrepareExpr((Expr *) qual, estate); } MemoryContextSwitchTo(oldContext); } /* * We will use the EState's per-tuple context for evaluating * constraint expressions (creating it if it's not already there). */ econtext = GetPerTupleExprContext(estate); /* Arrange for econtext's scan tuple to be the tuple under test */ econtext->ecxt_scantuple = slot; /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL92 specifies that a NULL result from a constraint * expression is not to be treated as a failure. Therefore, tell * ExecQual to return TRUE for NULL. */ if (!ExecQual(qual, econtext, true)) return check[i].ccname; } /* NULL result means no error */ return NULL;}voidExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate){ Relation rel = resultRelInfo->ri_RelationDesc; HeapTuple tuple = slot->val; TupleConstr *constr = rel->rd_att->constr; Assert(constr); if (constr->has_not_null) { int natts = rel->rd_att->natts; int attrChk; for (attrChk = 1; attrChk <= natts; attrChk++) { if (rel->rd_att->attrs[attrChk - 1]->attnotnull && heap_attisnull(tuple, attrChk)) ereport(ERROR, (errcode(ERRCODE_NOT_NULL_VIOLATION), errmsg("null value in column \"%s\" violates not-null constraint", NameStr(rel->rd_att->attrs[attrChk - 1]->attname)))); } } if (constr->num_check > 0) { const char *failed; if ((failed = ExecRelCheck(resultRelInfo, slot, estate)) != NULL) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("new row for relation \"%s\" violates check constraint \"%s\"", RelationGetRelationName(rel), failed))); }}/* * Check a modified tuple to see if we want to process its updated version * under READ COMMITTED rules. * * See backend/executor/README for some info about how this works. */TupleTableSlot *EvalPlanQual(EState *estate, Index rti, ItemPointer tid){ evalPlanQual *epq; EState *epqstate; Relation relation; HeapTupleData tuple; HeapTuple copyTuple = NULL; bool endNode; Assert(rti != 0); /* * find relation containing target tuple */ if (estate->es_result_relation_info != NULL && estate->es_result_relation_info->ri_RangeTableIndex == rti) relation = estate->es_result_relation_info->ri_RelationDesc; else { List *l; relation = NULL; foreach(l, estate->es_rowMark) { if (((execRowMark *) lfirst(l))->rti == rti) { relation = ((execRowMark *) lfirst(l))->relation; break; } } if (relation == NULL) elog(ERROR, "could not find RowMark for RT index %u", rti); } /* * fetch tid tuple * * Loop here to deal with updated or busy tuples */ tuple.t_self = *tid; for (;;) { Buffer buffer; if (heap_fetch(relation, SnapshotDirty, &tuple, &buffer, false, NULL)) { TransactionId xwait = SnapshotDirty->xmax; /* xmin should not be dirty... */ if (TransactionIdIsValid(SnapshotDirty->xmin)) elog(ERROR, "t_xmin is uncommitted in tuple to be updated"); /* * If tuple is being updated by other transaction then we have * to wait for its commit/abort. */ if (TransactionIdIsValid(xwait)) { ReleaseBuffer(buffer); XactLockTableWait(xwait); continue; } /* * We got tuple - now copy it for use by recheck query. */ copyTuple = heap_copytuple(&tuple); ReleaseBuffer(buffer); break; } /* * Oops! Invalid tuple. Have to check is it updated or deleted. * Note that it's possible to get invalid SnapshotDirty->tid if * tuple updated by this transaction. Have we to check this ? */ if (ItemPointerIsValid(&(SnapshotDirty->tid)) && !(ItemPointerEquals(&(tuple.t_self), &(SnapshotDirty->tid)))) { /* updated, so look at the updated copy */ tuple.t_self = SnapshotDirty->tid; continue; } /* * Deleted or updated by this transaction; forget it. */ return NULL; } /* * For UPDATE/DELETE we have to return tid of actual row we're * executing PQ for. */ *tid = tuple.t_self; /* * Need to run a recheck subquery. Find or create a PQ stack entry. */ epq = estate->es_evalPlanQual; endNode = true; if (epq != NULL && epq->rti == 0) { /* Top PQ stack entry is idle, so re-use it */ Assert(!(estate->es_useEvalPlan) && epq->next == NULL); epq->rti = rti; endNode = false; } /* * If this is request for another RTE - Ra, - then we have to check * wasn't PlanQual requested for Ra already and if so then Ra' row was * updated again and we have to re-start old execution for Ra and * forget all what we done after Ra was suspended. Cool? -:)) */ if (epq != NULL && epq->rti != rti && epq->estate->es_evTuple[rti - 1] != NULL) { do { evalPlanQual *oldepq; /* stop execution */ EvalPlanQualStop(epq); /* pop previous PlanQual from the stack */ oldepq = epq->next; Assert(oldepq && oldepq->rti != 0); /* push current PQ to freePQ stack */ oldepq->free = epq; epq = oldepq; estate->es_evalPlanQual = epq; } while (epq->rti != rti); } /* * If we are requested for another RTE then we have to suspend * execution of current PlanQual and start execution for new one. */ if (epq == NULL || epq->rti != rti) { /* try to reuse plan used previously */ evalPlanQual *newepq = (epq != NULL) ? epq->free : NULL; if (newepq == NULL) /* first call or freePQ stack is empty */ { newepq = (evalPlanQual *) palloc0(sizeof(evalPlanQual)); newepq->free = NULL; newepq->estate = NULL; newepq->planstate = NULL; } else { /* recycle previously used PlanQual */ Assert(newepq->estate == NULL); epq->free = NULL; } /* push current PQ to the stack */ newepq->next = epq; epq = newepq; estate->es_evalPlanQual = epq; epq->rti = rti; endNode = false; } Assert(epq->rti == rti); /* * Ok - we're requested for the same RTE. Unfortunately we still have * to end and restart execution of the plan, because ExecReScan * wouldn't ensure that upper plan nodes would reset themselves. We * could make that work if insertion of the target tuple were * integrated with the Param mechanism somehow, so that the upper plan * nodes know that their children's outputs have changed. * * Note that the stack of free evalPlanQual nodes is quite useless at the * moment, since it only saves us from pallocing/releasing the * evalPlanQual nodes themselves. But it will be useful once we * implement ReScan instead of end/restart for re-using PlanQual * nodes. */ if (endNode) { /* stop execution */ EvalPlanQualStop(epq); } /* * Initialize new recheck query. * * Note: if we were re-using PlanQual plans via ExecReScan, we'd need to * instead copy down changeable state from the top plan (including * es_result_relation_info, es_junkFilter) and reset locally * changeable state in the epq (including es_param_exec_vals, * es_evTupleNull). */ EvalPlanQualStart(epq, estate, epq->next); /* * free old RTE' tuple, if any, and store target tuple where * relation's scan node will see it */ epqstate = epq->estate; if (epqstate->es_evTuple[rti - 1] != NULL) heap_freetuple(epqstate->es_evTuple[rti - 1]); epqstate->es_evTuple[rti - 1] = copyTuple; return EvalPlanQualNext(estate);}static TupleTableSlot *EvalPlanQualNext(EState *estate){ evalPlanQual *epq = estate->es_evalPlanQual; MemoryContext oldcontext; TupleTableSlot *slot; Assert(epq->rti != 0);lpqnext:; oldcontext = MemoryContextSwitchTo(epq->estate->es_query_cxt); slot = ExecProcNode(epq->planstate); MemoryContextSwitchTo(oldcontext); /* * No more tuples for this PQ. Continue previous one. */ if (TupIsNull(slot)) { evalPlanQual *oldepq; /* stop execution */ EvalPlanQualStop(epq); /* pop old PQ from the stack */ oldepq = epq->next; if (oldepq == NULL) { /* this is the first (oldest) PQ - mark as free */ epq->rti = 0; estate->es_useEvalPlan = false; /* and continue Query execution */ return (NULL); } Assert(oldepq->rti != 0); /* push current PQ to freePQ stack */ oldepq->free = epq; epq = oldepq; estate->es_evalPlanQual = epq; goto lpqnext; } return (slot);}static voidEndEvalPlanQual(EState *estate){ evalPlanQual *epq = estate->es_evalPlanQual; if (epq->rti == 0) /* plans already shutdowned */ { Assert(epq->next == NULL); return; } for (;;) { evalPlanQual *oldepq; /* stop execution */ EvalPlanQualStop(epq); /* pop old PQ from the stack */ oldepq = epq->next; if (oldepq == NULL) { /* this is the first (oldest) PQ - mark as free */ epq->rti = 0; estate->es_useEvalPlan = false; break; } Assert(oldepq->rti != 0); /* push current PQ to freePQ stack */ oldepq->free = epq; epq = oldepq; estate->es_evalPlanQual = epq; }}/* * Start execution of one level of PlanQual. * * This is a cut-down version of ExecutorStart(): we copy some state from * the top-level estate rather than initializing it fresh. */static voidEvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq){ EState *epqstate; int rtsize; MemoryContext oldcontext; rtsize = length(estate->es_range_table); epq->estate = epqstate = CreateExecutorState(); oldcontext = MemoryContextSwitchTo(epqstate->es_query_cxt); /* * The epqstates share the top query's copy of unchanging state such * as the snapshot, rangetable, result-rel info, and external Param * info. They need their own copies of local state, including a tuple * table, es_param_exec_vals, etc. */ epqstate->es_direction = ForwardScanDirection; epqstate->es_snapshot = estate->es_snapshot; epqstate->es_crosscheck_snapshot = estate->es_crosscheck_snapshot; epqstate->es_range_table = estate->es_range_table; epqstate->es_result_relations = estate->es_result_relations; epqstate->es_num_result_relations = estate->es_num_result_relations; epqstate->es_result_relation_info = estate->es_result_relation_info; epqstate->es_junkFilter = estate->es_junkFilter; epqstate->es_into_relation_descriptor = estate->es_into_relation_descriptor; epqstate->es_param_list_info = estate->es_param_list_info; if (estate->es_topPlan->nParamExec > 0) epqstate->es_param_exec_vals = (ParamExecData *) palloc0(estate->es_topPlan->nParamExec * sizeof(ParamExecData)); epqstate->es_rowMark = estate->es_rowMark; epqstate->es_instrument = estate->es_instrument; epqstate->es_select_into = estate->es_select_into; epqstate->es_into_oids = estate->es_into_oids; epqstate->es_topPlan = estate->es_topPlan; /* * Each epqstate must have its own es_evTupleNull state, but all the * stack entries share es_evTuple state. This allows sub-rechecks to * inherit the value being examined by an outer recheck. */ epqstate->es_evTupleNull = (bool *) palloc0(rtsize * sizeof(bool)); if (priorepq == NULL) /* first PQ stack entry */ epqstate->es_evTuple = (HeapTuple *) palloc0(rtsize * sizeof(HeapTuple)); else /* later stack entries share the same storage */ epqstate->es_evTuple = priorepq->estate->es_evTuple; epqstate->es_tupleTable = ExecCreateTupleTable(estate->es_tupleTable->size); epq->planstate = ExecInitNode(estate->es_topPlan, epqstate); MemoryContextSwitchTo(oldcontext);}/* * End execution of one level of PlanQual. * * This is a cut-down version of ExecutorEnd(); basically we want to do most * of the normal cleanup, but *not* close result relations (which we are * just sharing from the outer query). */static voidEvalPlanQualStop(evalPlanQual *epq){ EState *epqstate = epq->estate; MemoryContext oldcontext; oldcontext = MemoryContextSwitchTo(epqstate->es_query_cxt); ExecEndNode(epq->planstate); ExecDropTupleTable(epqstate->es_tupleTable, true); epqstate->es_tupleTable = NULL; if (epqstate->es_evTuple[epq->rti - 1] != NULL) { heap_freetuple(epqstate->es_evTuple[epq->rti - 1]); epqstate->es_evTuple[epq->rti - 1] = NULL; } MemoryContextSwitchTo(oldcontext); FreeExecutorState(epqstate); epq->estate = NULL; epq->planstate = NULL;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -