PostgreSQL源码学习–删除数据#3

PostgreSQL源码学习–删除数据#3本节介绍ExecDelete函数。 从表中删除时,tupleid标识要删除的元组,oldtuple为空; 从视图中删除时,oldtuple传递给INSTEAD OF触发器标识要删除的内容,tuple…

PostgreSQL源码学习--删除数据#3

本节介绍ExecDelete函数。

从表中删除时,tupleid标识要删除的元组,oldtuple为空;

从视图中删除时,oldtuple传递给INSTEAD OF触发器标识要删除的内容,tupleid无效;

从外部表中删除时,tupleid无效,fdw使用planslot中的数据找出要删除的行,oldtuple传递给外部表触发器。

tupleDeleted标识是否删除了元组。 如果这个删除操作是分区键更新的一部分,则EvalPlanQual会使用输出参数epqslot将slot返回。

函数返回值为RETURNING的结果结果。

ExecDelete函数

static TupleTableSlot *
ExecDelete(ModifyTableState *mtstate,
		   ItemPointer tupleid,
		   HeapTuple oldtuple,
		   TupleTableSlot *planSlot,
		   EPQState *epqstate,
		   EState *estate,
		   bool processReturning,
		   bool canSetTag,
		   bool changingPart,
		   bool *tupleDeleted,
		   TupleTableSlot **epqreturnslot);

代码100分

代码100分//src/backend/executor/nodeModifyTable.c

/* 首先重置已删除的标志 */
if (tupleDeleted)
	*tupleDeleted = false;

/* 获取结果关系的信息 */
resultRelInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelInfo->ri_RelationDesc;

/* BEFORE ROW DELETE 触发器 */
if (resultRelInfo->ri_TrigDesc &&
	resultRelInfo->ri_TrigDesc->trig_delete_before_row)
{
	bool		dodelete;

	dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
							tupleid, oldtuple, epqreturnslot);

	if (!dodelete)		/* "do nothing" */
		return NULL;
}

/* INSTEAD OF ROW DELETE 触发器 */
if (resultRelInfo->ri_TrigDesc &&
	resultRelInfo->ri_TrigDesc->trig_delete_instead_row)
{
	bool		dodelete;

	Assert(oldtuple != NULL);
	dodelete = ExecIRDeleteTriggers(estate, resultRelInfo, oldtuple);

	if (!dodelete)		/* "do nothing" */
		return NULL;
}
/* 从外部表删除 */
else if (resultRelInfo->ri_FdwRoutine)
{
	/* 让fdw去做真正的删除 */
	slot = ExecGetReturningSlot(estate, resultRelInfo);
	slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate,
								   resultRelInfo,
								   slot,
								   planSlot);

	if (slot == NULL)		/* "do nothing" */
		return NULL;

	/* RETURNING表达式可能会用到tableoid,因此初始化一下tts_tableOid */
	if (TTS_EMPTY(slot))
		ExecStoreAllNullTuple(slot);

	slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
}
/* 一般的删除流程 */
else
{
/* goto跳跃点 */
ldelete:;
	/* 删除元组(上节讲过)*/
	result = table_tuple_delete(resultRelationDesc, tupleid,
						estate->es_output_cid,
						estate->es_snapshot,
						estate->es_crosscheck_snapshot,
						true /* wait for commit */ ,
						&tmfd,
						changingPart);
	
	switch (result)
	{
		/* 目标元组已经被当前命令或当前事务的后续命令删除了 */
		case TM_SelfModified:
			if (tmfd.cmax != estate->es_output_cid)
				ereport(ERROR,
					(errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
					 errmsg("tuple to be deleted was already modified by an operation triggered by the current command"),
					 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));

			/* 可能是当前命令的触发器或什么函数删除的,不报错但是不会返回给RETURNING */
			return NULL;
			
		/* 正常删除 */
		case TM_Ok:
			break;
			
		/* 目标元组已被另一个事务update */
		case TM_Updated:
			{
				/* 检查事务隔离级别是否大于等于可重复读 */
				if (IsolationUsesXactSnapshot())
					ereport(ERROR,
							(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
							 errmsg("could not serialize access due to concurrent update")));
				
				/* 已经知道要做EPQ,所以直接把tuple取到正确的slot */
				EvalPlanQualBegin(epqstate);
				inputslot = EvalPlanQualSlot(epqstate, resultRelationDesc,
										 resultRelInfo->ri_RangeTableIndex);

				/* 锁定最新版本的元组 */
				result = table_tuple_lock(resultRelationDesc, tupleid,
									  estate->es_snapshot,
									  inputslot, estate->es_output_cid,
									  LockTupleExclusive, LockWaitBlock,
									  TUPLE_LOCK_FLAG_FIND_LAST_VERSION,
									  &tmfd);
				
				switch (result)
				{
					case TM_Ok:
						Assert(tmfd.traversed);
						/* 判断是否应该在读已提交的规则下处理已更新的元组,如果为否的话,返回NULL */
						epqslot = EvalPlanQual(epqstate,
									   resultRelationDesc,
									   resultRelInfo->ri_RangeTableIndex,
									   inputslot);
						if (TupIsNull(epqslot))
							return NULL;
						
						/* 如果需要,跳过删除操作返回被更新的行 */
						if (epqreturnslot)
						{
							*epqreturnslot = epqslot;
							return NULL;
						}
						else
							goto ldelete;
					
					/* 若已经被自身这条命令更新,则跳过删除,否则报错 */
					case TM_SelfModified:
						if (tmfd.cmax != estate->es_output_cid)
							ereport(ERROR,
									(errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
									 errmsg("tuple to be deleted was already modified by an operation triggered by the current command"),
									 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
						return NULL;
						
					/* 若已经被删除 */
					case TM_Deleted:
						return NULL;
						
					/* 其它出错情况 */
					default:
						elog(ERROR, "unexpected table_tuple_lock status: %u",
							 result);
						return NULL;
				}
				
				Assert(false);
				break;
			}
			
		/* 目标元组已被另一个事务delete */
		case TM_Deleted:
			if (IsolationUsesXactSnapshot())
				ereport(ERROR,
						(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
						 errmsg("could not serialize access due to concurrent delete")));
			return NULL;
			
		/* 其它出错情况 */
		default:
			elog(ERROR, "unrecognized table_tuple_delete status: %u",
				 result);
			return NULL;
	}
	/* 索引的话等待vacuum清理就好 */
}

/* 有需要的话统计处理的元组数目 */
if (canSetTag)
	(estate->es_processed)++;

/* 可以设置已经成功删除的标记了 */
if (tupleDeleted)
	*tupleDeleted = true;

/* 如果此delete是将元组移动到新分区的分区键更新的过程,那么将此行放入转换旧表 */
ar_delete_trig_tcs = mtstate->mt_transition_capture;
if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture
	&& mtstate->mt_transition_capture->tcs_update_old_table)
{
	ExecARUpdateTriggers(estate, resultRelInfo,
					 tupleid,
					 oldtuple,
					 NULL,
					 NULL,
					 mtstate->mt_transition_capture);
	
	/* 这里已经捕获了新表的行,因此AFTER ROW DELETE触发器不应该再次捕获了 */
	ar_delete_trig_tcs = NULL;
}

/* AFTER ROW DELETE 触发器 */
ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldtuple,
				 ar_delete_trig_tcs);

/* 处理RETURNING */
if (processReturning && resultRelInfo->ri_projectReturning)
{
	/* 我们需要将一个元组放入slot中,因此需要先去取 */
	if (resultRelInfo->ri_FdwRoutine)
	{
		/* FDW 应该提供出来 */
		Assert(!TupIsNull(slot));
	}
	else
	{
		slot = ExecGetReturningSlot(estate, resultRelInfo);
		if (oldtuple != NULL)
		{
			ExecForceStoreHeapTuple(oldtuple, slot, false);
		}
		else
		{
			if (!table_tuple_fetch_row_version(resultRelationDesc, tupleid,
										   SnapshotAny, slot))
				elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
		}
	}
	rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
	
	/* 释放目标元组前先将rslot进行物化 */
	ExecMaterializeSlot(rslot);
	
	ExecClearTuple(slot);

	return rslot;
}

return NULL;

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/7979.html

(0)
上一篇 2023-03-10
下一篇 2023-03-10

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注