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);
//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;
以上是 PostgreSQL源码学习删除数据#3 的全部内容, 来源链接: utcz.com/z/534076.html