PostgreSQL源码学习插入数据#4,5

本节介绍table_tuple_insert和ExecInsert函数。
相关数据结构
//src/include/utils/rel.htypedef struct RelationData
{
	/* ...... */
	TupleDesc	rd_att;			/* tuple descriptor */
	/* ...... */
	/*
	 * Table access method.
	 */
	const struct TableAmRoutine *rd_tableam;
} RelationData;
//src/include/access/tupdesc.htypedef struct TupleDescData
{
	int			natts;			/* number of attributes in the tuple */
	Oid			tdtypeid;		/* composite type ID for tuple type */
	int32		tdtypmod;		/* typmod for tuple type */
	int			tdrefcount;		/* reference count, or -1 if not counting */
	TupleConstr *constr;		/* constraints, or NULL if none */
	/* attrs[N] is the description of Attribute Number N+1 */
	FormData_pg_attribute attrs[FLEXIBLE_ARRAY_MEMBER];
}			TupleDescData;
typedef struct TupleDescData *TupleDesc;
//src/include/nodes/parsenodes.h/*
 * WithCheckOption -
 *		representation of WITH CHECK OPTION checks to be applied to new tuples
 *		when inserting/updating an auto-updatable view, or RLS WITH CHECK
 *		policies to be applied when inserting/updating a relation with RLS.
 */
typedef enum WCOKind
{
	WCO_VIEW_CHECK,				/* WCO on an auto-updatable view */
	WCO_RLS_INSERT_CHECK,		/* RLS INSERT WITH CHECK policy */
	WCO_RLS_UPDATE_CHECK,		/* RLS UPDATE WITH CHECK policy */
	WCO_RLS_CONFLICT_CHECK		/* RLS ON CONFLICT DO UPDATE USING policy */
} WCOKind;
//src/include/nodes/nodes.htypedef enum OnConflictAction
{
	ONCONFLICT_NONE,			/* No "ON CONFLICT" clause */
	ONCONFLICT_NOTHING,			/* ON CONFLICT ... DO NOTHING */
	ONCONFLICT_UPDATE			/* ON CONFLICT ... DO UPDATE */
} OnConflictAction;
table_tuple_insert函数
//src/include/access/tableam.hstatic inline void
table_tuple_insert(Relation rel, TupleTableSlot *slot, CommandId cid,
				   int options, struct BulkInsertStateData *bistate)
{
	/* 默认的表存储引擎的话,rd_tableam赋值为heapam_methods,
	   其tuple_insert指向的就是上一节讲过的heapam_tuple_insert函数 */
	rel->rd_tableam->tuple_insert(rel, slot, cid, options,
							  bistate);
}
ExecInsert函数
//src/backend/executor/nodeModifyTable.c/* 物化slot */
ExecMaterializeSlot(slot);
/* 取得result relation相关信息 */
resultRelInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelInfo->ri_RelationDesc;
/* 执行BEFORE ROW INSERT触发器 */
if (resultRelInfo->ri_TrigDesc &&
	resultRelInfo->ri_TrigDesc->trig_insert_before_row)
{
	if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
		return NULL;		/* "do nothing" */
}
/* 如果设置了INSTEAD OF ROW触发器,执行触发器 */
if (resultRelInfo->ri_TrigDesc &&
	resultRelInfo->ri_TrigDesc->trig_insert_instead_row)
{
	if (!ExecIRInsertTriggers(estate, resultRelInfo, slot))
		return NULL;		/* "do nothing" */
}
/* 若没有INSTEAD OF ROW触发器,但是是外部表 */
else if (resultRelInfo->ri_FdwRoutine)
{
	/* 如果有生成列的话先计算生成列 */
	if (resultRelationDesc->rd_att->constr &&
		resultRelationDesc->rd_att->constr->has_generated_stored)
		ExecComputeStoredGenerated(estate, slot);
	/* 执行外部表的insert流程 */
	slot = resultRelInfo->ri_FdwRoutine->ExecForeignInsert(estate,
								   resultRelInfo,
								   slot,
								   planSlot);
	if (slot == NULL)		/* "do nothing" */
		return NULL;
	/* AFTER ROW触发器或RETURNING约束表达式可能会用到tuple的tableoid,所以这里提前给它赋值一下 */
	slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
}
/* 若没有INSTEAD OF ROW触发器,也不是外部表,走一般流程 */
else
{
	/* 可能有约束会用到tableoid,所以提前对它进行赋值 */
	slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
	/* 如果有的话计算生成列的数据 */
	if (resultRelationDesc->rd_att->constr &&
		resultRelationDesc->rd_att->constr->has_generated_stored)
		ExecComputeStoredGenerated(estate, slot);、
	/* 根据操作类型确定需要执行哪种行安全策略检查 */
		wco_kind = (mtstate->operation == CMD_UPDATE) ?
		WCO_RLS_UPDATE_CHECK : WCO_RLS_INSERT_CHECK;
	/* 执行Policy中的WithCheckOptions检查,在此函数中只进行wco_kind指定的类型检查 */
	if (resultRelInfo->ri_WithCheckOptions != NIL)
		ExecWithCheckOptions(wco_kind, resultRelInfo, slot, estate);
	/* 执行tuple的约束检查(例如NOT NULL这种) */
	if (resultRelationDesc->rd_att->constr)
		ExecConstraints(resultRelInfo, slot, estate);
	/* 若有分区表约束,执行分区表约束检查 */
	if (resultRelInfo->ri_PartitionCheck &&
		(resultRelInfo->ri_PartitionRoot == NULL ||
		 (resultRelInfo->ri_TrigDesc &&
		  resultRelInfo->ri_TrigDesc->trig_insert_before_row)))
		ExecPartitionCheck(resultRelInfo, slot, estate, true);
	/* 语句中含有ON CONFLICT ..., 并且表拥有索引时的情况 */
	if (onconflict != ONCONFLICT_NONE && resultRelInfo->ri_NumIndices > 0)
	{
		/* 获取执行约束检查的索引列表 */
		arbiterIndexes = resultRelInfo->ri_onConflictArbiterIndexes;
vlock:	/* goto返回点 */
		/* 先在这些索引列表上执行一个粗略的冲突检查 */
		specConflict = false;
		if (!ExecCheckIndexConstraints(slot, estate, &conflictTid,
							   arbiterIndexes))
		{
			/* 若检查到冲突,并且语句为ON CONFLICT...DO UPDATE */
			if (onconflict == ONCONFLICT_UPDATE)
			{
				/* 执行ON CONFLICT的UPDATE */
				if (ExecOnConflictUpdate(mtstate, resultRelInfo,
							 &conflictTid, planSlot, slot,
							 estate, canSetTag, &returning))
					{
						/* 计数+1,返回结果 */
						InstrCountTuples2(&mtstate->ps, 1);
						return returning;
					}
				else
					/* 若没有成功执行UPDATE,回到前面重新开始 */
					goto vlock;
			}
			/* 若有冲突,语句是ON CONFLICT DO NOTHING */
			else
			{
				/* 如同语句意思什么也不做,但是需要验证一下在更高的隔离级别中,
				   元组在MVCC快照中对执行器是可见的 */
				Assert(onconflict == ONCONFLICT_NOTHING);
				ExecCheckTIDVisible(estate, resultRelInfo, &conflictTid,
						ExecGetReturningSlot(estate, resultRelInfo));
				/* 计数+1,返回NULL */
				InstrCountTuples2(&mtstate->ps, 1);
				return NULL;
			}
		}
		/* 在开始插入之前,先获取“推测插入锁”,其它流程可以通过它
		   来等待我们决定是否插入,而不用等待我们整个事务结束 */
		specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
		/* 插入数据,默认存储引擎的话,这里调用heapam_tuple_insert_speculative函数 */
		table_tuple_insert_speculative(resultRelationDesc, slot,
							   estate->es_output_cid,
							   0,
							   NULL,
							   specToken);
		/* 为新插入的元组更新索引 */
		recheckIndexes = ExecInsertIndexTuples(slot, estate, true,
							   &specConflict,
							   arbiterIndexes);
		/* 调整元组的状态,默认存储引擎的话,这里调用heapam_tuple_complete_speculative函数 */
		table_tuple_complete_speculative(resultRelationDesc, slot,
							 specToken, !specConflict);
		/* 唤醒其它在等待此元组决定是否插入的流程,可以根据这里的选择继续流程了 */
		SpeculativeInsertionLockRelease(GetCurrentTransactionId());
		/* 如果有冲突,跳到前面重新开始,因为在ExecInsertIndexTuples流程中
		   已经把有冲突的项加进去了,所以再次检测的话可以发现冲突 */
		if (specConflict)
		{
			list_free(recheckIndexes);
			goto vlock;
		}
		/* 若没有冲突,说明已经成功完成了ON CONFLICT的流程 */
	}
	/* 没有ON CONFLICT语句的话,走正常插入流程 */
	else
	{
		/* 插入元组,上面讲了此函数 */
		table_tuple_insert(resultRelationDesc, slot,
					   estate->es_output_cid,
					   0, NULL);
		if (resultRelInfo->ri_NumIndices > 0)
			recheckIndexes = ExecInsertIndexTuples(slot, estate, false, NULL,
										   NIL);
	}
}
/* 更新计数和last tid */
if (canSetTag)
{
	(estate->es_processed)++;
	setLastTid(&slot->tts_tid);
}
/* 如果本次插入是由于分区键更新带来的,需要执行额外流程 */
ar_insert_trig_tcs = mtstate->mt_transition_capture;
if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture
	&& mtstate->mt_transition_capture->tcs_update_new_table)
{
	ExecARUpdateTriggers(estate, resultRelInfo, NULL,
						 NULL,
						 slot,
						 NULL,
						 mtstate->mt_transition_capture);
	ar_insert_trig_tcs = NULL;
}
/* 执行AFTER ROW INSERT触发器 */
ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
				 ar_insert_trig_tcs);
list_free(recheckIndexes);
/* 检查父视图的WITH CHECK OPTIONS约束 */
if (resultRelInfo->ri_WithCheckOptions != NIL)
	ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
/* 处理RETURNING */
if (resultRelInfo->ri_projectReturning)
	result = ExecProcessReturning(resultRelInfo, slot, planSlot);
return result;
以上是 PostgreSQL源码学习插入数据#4,5 的全部内容, 来源链接: utcz.com/z/533236.html








