postgres源码解析40 表创建执行全流程梳理--4

news2024/9/21 18:33:07

本文讲解非系统表的创建逻辑([<fontcolor=0000dd>普通表和索引表]),其入口函数为heap_create,内部公共接口函数为RelationBuildLocalRelation和RelationCreateStorage相关知识回顾见:
postgres源码解析38 表创建执行全流程梳理–1
postgres源码解析38 表创建执行全流程梳理–2
postgres源码解析38 表创建执行全流程梳理–3

heap_create 执行逻辑

在这里插入图片描述
1)首先进行安全性检查,不允许在系统表中创建relations,判断是否需要创建持久化文件等;
2)根据表名、表空间、表对象标识符和文件节点relfilenode等信息调用 RelationBuildLocalRelation在内存中构建Relation,并插入全局relcache 哈希表中;
3)结合relation类型调用相应的接口函数进行relation的创建,[普通表/TOAST/物化视图: table_relation_set_new_filenode,索引/序列:RelationCreateStorage];
4) 对于无需创建持久化的relation且用户指定表空间,则需要在 pg_tablespace 中注册对应的信息。

RelationBuildLocalRelation

该函数目的是在内存中构建创建表的relcache Entry,并插入全局Relcache 哈希表中,用于加速后续对此表的访问。
1)如果不存在CacheMemoryContext,则创建此上下文,后续操作均在此上下文进行;
2)分配并初始化Relation结构体,结合入参的TueDesc填充Relation结构体中rd_att字段:字段属性的详细信息;
3)分配并根据入参填充Relation结构体中rd_att字段的Form_pg_class字段:表名、命名空间、字段属性/数目等;
4)调用 RelationInitLockInfo初始化relation描述符锁信息;
5)调用 RelationInitPhysicalAddr 初始化relation描述符对应的物理地址:spcNode/dbNode//RelNode [表空间/数据库/表]
6)将上述构建好的RelCache Entry插入全局ralcache 哈希表中,并增加该条目的引用计数
在这里插入图片描述

*
 *		RelationBuildLocalRelation
 *			Build a relcache entry for an about-to-be-created relation,
 *			and enter it into the relcache.
 */
Relation
RelationBuildLocalRelation(const char *relname,
						   Oid relnamespace,
						   TupleDesc tupDesc,
						   Oid relid,
						   Oid accessmtd,
						   Oid relfilenode,
						   Oid reltablespace,
						   bool shared_relation,
						   bool mapped_relation,
						   char relpersistence,
						   char relkind)
{
	Relation	rel;
	MemoryContext oldcxt;
	int			natts = tupDesc->natts;
	int			i;
	bool		has_not_null;
	bool		nailit;

	AssertArg(natts >= 0);

	/*
	 * check for creation of a rel that must be nailed in cache.
	 *
	 * XXX this list had better match the relations specially handled in
	 * RelationCacheInitializePhase2/3.
	 */
	switch (relid)
	{
		case DatabaseRelationId:
		case AuthIdRelationId:
		case AuthMemRelationId:
		case RelationRelationId:
		case AttributeRelationId:
		case ProcedureRelationId:
		case TypeRelationId:
			nailit = true;
			break;
		default:
			nailit = false;
			break;
	}

	/*
	 * check that hardwired list of shared rels matches what's in the
	 * bootstrap .bki file.  If you get a failure here during initdb, you
	 * probably need to fix IsSharedRelation() to match whatever you've done
	 * to the set of shared relations.
	 */
	if (shared_relation != IsSharedRelation(relid))
		elog(ERROR, "shared_relation flag for \"%s\" does not match IsSharedRelation(%u)",
			 relname, relid);

	/* Shared relations had better be mapped, too */
	Assert(mapped_relation || !shared_relation);

	/*
	 * switch to the cache context to create the relcache entry.
	 */
	if (!CacheMemoryContext)
		CreateCacheMemoryContext();

	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);

	/*
	 * allocate a new relation descriptor and fill in basic state fields.
	 */
	rel = (Relation) palloc0(sizeof(RelationData));

	/* make sure relation is marked as having no open file yet */
	rel->rd_smgr = NULL;

	/* mark it nailed if appropriate */
	rel->rd_isnailed = nailit;

	rel->rd_refcnt = nailit ? 1 : 0;

	/* it's being created in this transaction */
	rel->rd_createSubid = GetCurrentSubTransactionId();
	rel->rd_newRelfilenodeSubid = InvalidSubTransactionId;
	rel->rd_firstRelfilenodeSubid = InvalidSubTransactionId;
	rel->rd_droppedSubid = InvalidSubTransactionId;

	/*
	 * create a new tuple descriptor from the one passed in.  We do this
	 * partly to copy it into the cache context, and partly because the new
	 * relation can't have any defaults or constraints yet; they have to be
	 * added in later steps, because they require additions to multiple system
	 * catalogs.  We can copy attnotnull constraints here, however.
	 */
	rel->rd_att = CreateTupleDescCopy(tupDesc);
	rel->rd_att->tdrefcount = 1;	/* mark as refcounted */
	has_not_null = false;
	for (i = 0; i < natts; i++)
	{
		Form_pg_attribute satt = TupleDescAttr(tupDesc, i);
		Form_pg_attribute datt = TupleDescAttr(rel->rd_att, i);

		datt->attidentity = satt->attidentity;
		datt->attgenerated = satt->attgenerated;
		datt->attnotnull = satt->attnotnull;
		has_not_null |= satt->attnotnull;
	}

	if (has_not_null)
	{
		TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));

		constr->has_not_null = true;
		rel->rd_att->constr = constr;
	}

	/*
	 * initialize relation tuple form (caller may add/override data later)
	 */
	rel->rd_rel = (Form_pg_class) palloc0(CLASS_TUPLE_SIZE);

	namestrcpy(&rel->rd_rel->relname, relname);
	rel->rd_rel->relnamespace = relnamespace;

	rel->rd_rel->relkind = relkind;
	rel->rd_rel->relnatts = natts;
	rel->rd_rel->reltype = InvalidOid;
	/* needed when bootstrapping: */
	rel->rd_rel->relowner = BOOTSTRAP_SUPERUSERID;

	/* set up persistence and relcache fields dependent on it */
	rel->rd_rel->relpersistence = relpersistence;
	switch (relpersistence)
	{
		case RELPERSISTENCE_UNLOGGED:
		case RELPERSISTENCE_PERMANENT:
			rel->rd_backend = InvalidBackendId;
			rel->rd_islocaltemp = false;
			break;
		case RELPERSISTENCE_TEMP:
			Assert(isTempOrTempToastNamespace(relnamespace));
			rel->rd_backend = BackendIdForTempRelations();
			rel->rd_islocaltemp = true;
			break;
		default:
			elog(ERROR, "invalid relpersistence: %c", relpersistence);
			break;
	}

	/* if it's a materialized view, it's not populated initially */
	if (relkind == RELKIND_MATVIEW)
		rel->rd_rel->relispopulated = false;
	else
		rel->rd_rel->relispopulated = true;

	/* set replica identity -- system catalogs and non-tables don't have one */
	if (!IsCatalogNamespace(relnamespace) &&
		(relkind == RELKIND_RELATION ||
		 relkind == RELKIND_MATVIEW ||
		 relkind == RELKIND_PARTITIONED_TABLE))
		rel->rd_rel->relreplident = REPLICA_IDENTITY_DEFAULT;
	else
		rel->rd_rel->relreplident = REPLICA_IDENTITY_NOTHING;

	/*
	 * Insert relation physical and logical identifiers (OIDs) into the right
	 * places.  For a mapped relation, we set relfilenode to zero and rely on
	 * RelationInitPhysicalAddr to consult the map.
	 */
	rel->rd_rel->relisshared = shared_relation;

	RelationGetRelid(rel) = relid;

	for (i = 0; i < natts; i++)
		TupleDescAttr(rel->rd_att, i)->attrelid = relid;

	rel->rd_rel->reltablespace = reltablespace;

	if (mapped_relation)
	{
		rel->rd_rel->relfilenode = InvalidOid;
		/* Add it to the active mapping information */
		RelationMapUpdateMap(relid, relfilenode, shared_relation, true);
	}
	else
		rel->rd_rel->relfilenode = relfilenode;

	RelationInitLockInfo(rel);	/* see lmgr.c */

	RelationInitPhysicalAddr(rel);

	rel->rd_rel->relam = accessmtd;

	/*
	 * RelationInitTableAccessMethod will do syscache lookups, so we mustn't
	 * run it in CacheMemoryContext.  Fortunately, the remaining steps don't
	 * require a long-lived current context.
	 */
	MemoryContextSwitchTo(oldcxt);

	if (relkind == RELKIND_RELATION ||
		relkind == RELKIND_SEQUENCE ||
		relkind == RELKIND_TOASTVALUE ||
		relkind == RELKIND_MATVIEW)
		RelationInitTableAccessMethod(rel);

	/*
	 * Okay to insert into the relcache hash table.
	 *
	 * Ordinarily, there should certainly not be an existing hash entry for
	 * the same OID; but during bootstrap, when we create a "real" relcache
	 * entry for one of the bootstrap relations, we'll be overwriting the
	 * phony one created with formrdesc.  So allow that to happen for nailed
	 * rels.
	 */
	RelationCacheInsert(rel, nailit);

	/*
	 * Flag relation as needing eoxact cleanup (to clear rd_createSubid). We
	 * can't do this before storing relid in it.
	 */
	EOXactListAdd(rel);

	/* It's fully valid */
	rel->rd_isvalid = true;

	/*
	 * Caller expects us to pin the returned entry.
	 */
	RelationIncrementReferenceCount(rel);

	return rel;
}

RelationCreateStorage
物理文件的创建由磁盘管理器负责,pg中所有文件系统均调用这统一接口,而RelationCreateStorage 函数的实现就是通过调用这些函数进一步封装而成,期执行流程如下:
1)对于持久化的relation,设置字段表示need_wal,表明需要写WAL日志,对于临时relation或者unlogged relation无需此操作;
2)根据输入的RelFileNode调用 smgropen返回 SMgrRelation对象,不存在会创建一个;
3)结合上述返回的 SMgrRelation和ForkNumber号调用 smgrcreate创建relation的物理文件;
4)如需写WAL日志,调用 log_smgrcreate函数记录下此relation的实际物理信息;
5)最后将其添加至PendingRelDelete链表尾,在事务真正提交的时候如需回滚则可通过此信息将创建的文件删除,并返回 SMgrRelation对象。

在这里插入图片描述

/*
 * RelationCreateStorage
 *		Create physical storage for a relation.
 *
 * Create the underlying disk file storage for the relation. This only
 * creates the main fork; additional forks are created lazily by the
 * modules that need them.
 *
 * This function is transactional. The creation is WAL-logged, and if the
 * transaction aborts later on, the storage will be destroyed.
 */
SMgrRelation
RelationCreateStorage(RelFileNode rnode, char relpersistence)
{
	PendingRelDelete *pending;
	SMgrRelation srel;
	BackendId	backend;
	bool		needs_wal;

	Assert(!IsInParallelMode());	/* couldn't update pendingSyncHash */

	switch (relpersistence)
	{
		case RELPERSISTENCE_TEMP:
			backend = BackendIdForTempRelations();
			needs_wal = false;
			break;
		case RELPERSISTENCE_UNLOGGED:
			backend = InvalidBackendId;
			needs_wal = false;
			break;
		case RELPERSISTENCE_PERMANENT:
			backend = InvalidBackendId;
			needs_wal = true;
			break;
		default:
			elog(ERROR, "invalid relpersistence: %c", relpersistence);
			return NULL;		/* placate compiler */
	}

	srel = smgropen(rnode, backend);
	smgrcreate(srel, MAIN_FORKNUM, false);

	if (needs_wal)
		log_smgrcreate(&srel->smgr_rnode.node, MAIN_FORKNUM);

	/* Add the relation to the list of stuff to delete at abort */
	pending = (PendingRelDelete *)
		MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
	pending->relnode = rnode;
	pending->backend = backend;
	pending->atCommit = false;	/* delete if abort */
	pending->nestLevel = GetCurrentTransactionNestLevel();
	pending->next = pendingDeletes;
	pendingDeletes = pending;

	if (relpersistence == RELPERSISTENCE_PERMANENT && !XLogIsNeeded())
	{
		Assert(backend == InvalidBackendId);
		AddPendingSync(&rnode);
	}

	return srel;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/57849.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mac M1使用UTM安装centos7 x86_64虚拟机

一、环境说明 1. 宿主机环境 macbook m1 pro 16G 2. UTM版本 UTM是基于QEMU的系统模拟器和虚拟机主机&#xff0c;适用于iOS和macOS。 UTM is a full featured system emulator and virtual machine host for iOS and macOS. It is based off of QEMU. 最新版下载地址&…

带你玩转序列模型之NLP与词嵌入(二)

目录 一.Word2Vec 二.负采样 三.GloVe词向量 四.情绪分类 五.词嵌入除偏 一.Word2Vec 在上个视频中你已经见到了如何学习一个神经语言模型来得到更好的词嵌入&#xff0c;在本视频中你会见到 Word2Vec算法&#xff0c;这是一种简单而且计算时更加高效的方式来学习这种类…

用于 Python 降维的主成分分析

减少预测模型的输入变量数称为降维。 较少的输入变量可以产生更简单的预测模型&#xff0c;该模型在对新数据进行预测时可能具有更好的性能。 也许机器学习中最流行的降维技术是主成分分析&#xff0c;简称PCA。这是一种来自线性代数领域的技术&#xff0c;可用作数据准备技术…

耗时大半个月收整全套「Java架构进阶pdf」

花了我大半个月时间收整了全套的「Java架构进阶pdf」&#xff0c;这一波下来&#xff0c;刷完你就会知道&#xff0c;真真香啊&#xff0c;我的心血果然&#xff0c;没白费&#xff01; 请注意&#xff1a;关于全套的「Java架构进阶pdf」&#xff0c;我会从面试-筑基-框架-分布…

【Android App】实战项目之仿微信的视频通话(附源码和演示 超详细必看)

需要源码请点赞关注收藏后评论区留言私信~~~ 虽然手机出现许多年了&#xff0c;它具备的功能也越来越丰富&#xff0c;但是最基本的通话功能几乎没有变化。从前使用固定电话的时候&#xff0c;通话就是听声音&#xff1b;如今使用最新的智能手机&#xff0c;通话仍旧是听声音。…

无刷三相直流电机电动工具驱动方案设计

电动工具是一种工具&#xff0c;其致动通过附加的动力源和机构比仅其他手工劳动与使用手工工具&#xff0c;电动工具用于工业、建筑、花园、做饭、清洁等家务劳动&#xff0c;以及在房子周围用于驱动&#xff08;紧固件&#xff09;、钻孔、切割、成型、打磨、研磨、布线、抛光…

【微服务】Java agent 使用详解

一、前言 于一个即将上线的应用来说&#xff0c;系统监控是必不可少的&#xff0c;为什么需要监控呢&#xff1f;应用是跑在服务器上的&#xff0c;应用在运行过程中会发生各自意想不到的问题&#xff0c;像大家熟知的OOM&#xff0c;mysql故障&#xff0c;服务器宕机&#xff…

cubeIDE开发,基于已有的STM32CubeMX (.ioc)创建工程文件

一、STM32Cube 生态系统 可以在其官网查看&#xff0c;支持中文。 STM32Cube - Discover the STM32Cube Ecosystem - STMicroelectronics ​ 截取官网的STM32Cube家族的软件工具描述&#xff1a; 【1】STM32CubeMX, 面向任意STM32设备的配置工具。这款简单易用的图形用户界面为…

ubuntu根目录清理

0.防范于未然&#xff08;就像给window电脑清理垃圾&#xff09; 清理ubuntu用不上的东西的常用命令 # 系统自带清理命令 sudo apt-get autoclean sudo apt-get clean sudo apt-get autoremove# 查看目录占用空间 sudo du -cks * | sort -rn | head -10 sudo du --max-depth1…

[附源码]计算机毕业设计JAVA血库管理系统

[附源码]计算机毕业设计JAVA血库管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Ma…

Effective C++条款24:若所有参数皆需类型转换,请为此采用non-member函数

Effective C条款24&#xff1a;若所有参数皆需类型转换&#xff0c;请为此采用non-member函数&#xff08;Declare non-member functions when type conversions should apply to all parameters&#xff09;条款24&#xff1a;若所有参数皆需类型转换&#xff0c;请为此采用no…

Swift基础语法 - 枚举

枚举的基本用法 定义&#xff1a;枚举简单的说也是一种数据类型&#xff0c;只不过是这种数据类型只包含自定义的特定数据&#xff0c;它是一组有共同特性的数据的集合。 enum Direction {case northcase southcase eastcase west }enum Direction {case north,south,east,we…

数理统计笔记10:回归分析

引言 数理统计笔记的第10篇介绍了回归分析&#xff0c;从相关关系开始介绍&#xff0c;然后介绍回归分析&#xff0c;主要介绍了一元回归模型和多元回归模型&#xff0c;并对其中的原理和检验进行了叙述&#xff0c;最后简单介绍了一下可以化为线性回归模型的非线性回归模型。 …

【Gradle-5】Gradle常用命令与参数

1、前言 Gradle的命令有很多&#xff0c;熟悉常用命令之后&#xff0c;在日常开发中&#xff0c;不仅可以提升效率&#xff0c;也可以辅助我们快速定位并解决编译问题&#xff1b;而且某些情况下命令行(CLI)与按钮执行的编译结果是不一样的&#xff0c;比如构建时要传参(-P)&a…

大学生环保主题网页制作 环境网页设计模板 学生静态网页作业成品 dreamweaver保护地球环境HTML网站制作

&#x1f380; 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

【电商项目实战】上传头像(详细篇)

&#x1f341;博客主页&#xff1a;&#x1f449;不会压弯的小飞侠 ✨欢迎关注&#xff1a;&#x1f449;点赞&#x1f44d;收藏⭐留言✒ ✨系列专栏&#xff1a;&#x1f449;SpringBoot电商项目实战 ✨学习社区&#xff1a; &#x1f449;不会压弯的小飞侠 ✨知足上进&#x…

【Spring源码系列】Bean生命周期-实例化前

这里写目录标题前言一、实例化前 - InstantiationAwareBeanPostProcessor介绍InstantiationAwareBeanPostProcessor实例化前作用InstantiationAwareBeanPostProcessor实例化前代码案例二、实例化前 - 源码分析声明关键点源代码解读前言 在Bean的生命周期中&#xff0c;‘实例化…

Python还是很迷茫的小伙伴进来,教你用图秒懂Python

哈喽&#xff0c;大家好呀&#xff01;今天为大家带来12张图解python&#xff0c;让你们轻松学会了解python。 1.Python 解释器&#xff1a; Python数据结构&#xff1a;变量与运算符&#xff1a;Python 流程控制&#xff1a;Python 文件处理&#xff1a;python 输入输出&…

(三)Vue之模板语法

文章目录插值语法指令语法Vue学习目录上一篇&#xff1a;&#xff08;二&#xff09;初识Vue 下一篇&#xff1a;&#xff08;四&#xff09;Vue之数据绑定 Vue模板语法有2大类&#xff1a; 1.插值语法2.指令语法 插值语法 功能&#xff1a;用于解析标签体内容。 写法&…

lombok入门

目录 lombok概述 lombok安装 Getter、Setter ToString EqualsAndHashCode NotNull 生成构造方法相关注解 Data、Builder Log Cleanup、SneakyThrows lombok概述 以前的Java项目中&#xff0c;充斥着太多不友好的代码&#xff1a;POJO的getter/setter/toString/构造方…