⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 execmain.c

📁 PostgreSQL7.4.6 for Linux
💻 C
📖 第 1 页 / 共 4 页
字号:
			{				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 + -