PostgreSQL源码学习删除数据#3

database

本节介绍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

回到顶部