PostgreSQL源码学习–插入数据#4,5[亲测有效]

PostgreSQL源码学习–插入数据#4,5[亲测有效]本节介绍table_tuple_insert和ExecInsert函数。 相关数据结构 //src/include/utils/rel.h typedef struct RelationData {…

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

本节介绍table_tuple_insert和ExecInsert函数。

相关数据结构

//src/include/utils/rel.h

typedef struct RelationData
{
	/* ...... */
	TupleDesc	rd_att;			/* tuple descriptor */
	/* ...... */
	/*
	 * Table access method.
	 */
	const struct TableAmRoutine *rd_tableam;
} RelationData;

代码100分

代码100分//src/include/access/tupdesc.h

typedef 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;
代码100分//src/include/nodes/nodes.h

typedef 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.h

static 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;

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

(0)
上一篇 2023-02-19
下一篇 2023-02-20

相关推荐

  • Python数组如何转换为列表?

    Python数组如何转换为列表?Python中数组是采用NumPy库实现的。Python数组可以通过dtype参数指定元素的数据类型,而列表是一种基本数据类型。因此,在使用Python编程时,经常需要将数组转换为列表,这样我们就可以更方便地使用Python的各种功能来操作数组。

    2024-09-02
    23
  • int型参数的SQL注入

    int型参数的SQL注入类似PHP语言的 mysql_real_escape_string() 的函数,在用来防范SQL注入的时候,可能会遇到int型注入成功的情况。 mysql_real_escape_string()用法

    2023-02-10
    174
  • MySQL中的外键是什么、有什么作用「建议收藏」

    MySQL中的外键是什么、有什么作用「建议收藏」MySQL外键的作用: 保持数据一致性,完整性,主要目的是控制存储在外键表中的数据。使两张表形成关联,外键只能引用外表中列的值! 我们来建两个表 CREATE TABLE `example1` ( …

    2023-04-04
    157
  • mysql sum函数性能_select count(0)

    mysql sum函数性能_select count(0)MySQL 对window函数执行sum函数疑似Bug 使用MySql的窗口函数统计数据时,发现一个小的问题,与大家一起探讨下。 环境配置: mysql-installer-community-8.0

    2023-03-19
    180
  • 时序数据库 Apache-IoTDB 源码解析之前言(一)[通俗易懂]

    时序数据库 Apache-IoTDB 源码解析之前言(一)[通俗易懂]IoTDB 是一款时序数据库,相关竞品有 Kairosdb,InfluxDB,TimescaleDB等,主要使用场景是在物联网相关行业,如:车联网、风力发电、地铁、飞机监控等等,具体应用案例及公司详…

    2023-01-26
    151
  • C#使用MongoDB「终于解决」

    C#使用MongoDB「终于解决」MongoDB是啥? 简单来说:MongoDB是一种非关系型数据库(NoSQL)。 进一步来说:MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据…

    2023-03-31
    151
  • Python开方操作

    Python开方操作Python作为一门高级编程语言,已被广泛使用于各种计算机领域。其简洁、可读性强的代码以及丰富的库,大大地提高了编程效率。Python中有很多数学操作都可以方便地实现,例如,开方操作。

    2024-07-25
    38
  • 分享 | 一文了解 PG PITR 即时恢复「终于解决」

    分享 | 一文了解 PG PITR 即时恢复「终于解决」作者:颜博 青云科技数据库研发工程师 目前从事 PostgreSQL 产品开发工作,热衷于 PostgreSQL 数据库的学习和研究 在数据库系统中,数据是一切的基础,数据的安全更是重中之重。但可能因

    2023-04-26
    154

发表回复

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