IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> Postgresql源码(48)namespace计算逻辑分析(不显示指定namespace) -> 正文阅读

[大数据]Postgresql源码(48)namespace计算逻辑分析(不显示指定namespace)

搜索优先级

默认情况下查询一张表,不指定namespace,系统查询的是哪个namespace?

"$user", public场景来说,搜索顺序:

1、临时表
2、系统表
3、普通表

(原因见下面recomputeNamespacePath函数分析)

postgres=# show search_path ;
   search_path   
-----------------
 "$user", public

-- 优先级1:临时表
-- create temp table pg_class(i int);
pg_temp_3.pg_class

-- 优先级2:pg_catalog
pg_catalog.pg_class

-- 优先级3:普通表
-- create table pg_class(a int);
public.pg_class

namespace核心全局变量

1 规律

namespace大部分逻辑都是在维护、使用三个全局变量:

  1. 搜索空间:activeSearchPath
  2. 创建空间:activeCreationNamespace
  3. 临时空间:activeTempCreationPending

三个全局变量有两套赋值逻辑:

第一套:计算好baseXXX,然后统一赋值给activeXXX,相当于激活使用base

		activeSearchPath = baseSearchPath;
		activeCreationNamespace = baseCreationNamespace;
		activeTempCreationPending = baseTempCreationPending;

第二套:用临时值给三个变量赋值,相当于临时切换到另一套查找空间,用完了可以切换回第一套

	activeSearchPath = entry->searchPath;
	activeCreationNamespace = entry->creationNamespace;
	activeTempCreationPending = false;	/* XXX is this OK? */

注意:

  • namespace的所有工具函数都会直接使用activeXXX激活的全局变量
  • recomputeNamespacePath函数负责计算他们

2 细节

  • 通常使用namespace_search_path:$1 = 0x282df20 "\"$user\", public"(文本)来搜索命名空间。
  • 对于某些特殊代码区域,配置override stack可以忽略namespace_search_path。
  • 真正用于搜索使用的是activeSearchPath(List),activeSearchPath指向只有两种情况
    • 情况一:指向override stack堆顶
    • 情况二:指向baseSearchPath(List,从namespace_search_path构造而来)

activeSearchPath长什么样?

activeSearchPath
(gdb) p activeSearchPath->elements[0]->oid_value
$9 = 24709
(gdb) p activeSearchPath->elements[1]->oid_value
$10 = 11
(gdb) p activeSearchPath->elements[2]->oid_value
$11 = 2200
  • baseSearchPath的可用性由baseSearchPathValid(Bool)表示,如果失效需要重新从namespace_search_path字符串计算。
  • 什么时候失效?
    • 情况一:namespace_search_path变了
    • 情况二:pg_namespace的syscache失效
  • 什么时候重新算?
    • override查找时重新计算

baseSearchPath长什么样?

(gdb) p baseSearchPath->elements[0]->oid_value
$14 = 24709
(gdb) p baseSearchPath->elements[1]->oid_value
$15 = 11
(gdb) p baseSearchPath->elements[2]->oid_value
$16 = 2200
  • activeSearchPath/activeCreationNamespace/activeTempCreationPending变化时+1
  • 用于快速判断上述值是否改变

核心函数分析:recomputeNamespacePath

recomputeNamespacePath在namespace.c中有20多次调用,下面分析执行流程:

SQL执行中打开任意表文件执行RelnameGetRelid时,会第一次执行recomputeNamespacePath,后面还会执行多次recomputeNamespacePath但不会重新计算了。

第一次执行总结:

  • 【1】第一次进函数时,baseSearchPathValid是false,需要重新计算baseSearchPath(List)。注意这里还有一个判断当前用户是不是切换了,切换了也要重新刷baseSearchPath。一般这是namespace_search_path字符串都有值,就是search_path,baseSearchPath就是从这个字符串计算出来的。

  • 【2】namespace_search_path字符串组装到namelist中"\"$user\", public" -------> [$user][public]

  • 【3】开始解析namelist

    • 当前【$user】如果是$user有同名的namespace在pg_namespace中,可以查到。但是一般我们只创建用户,不会默认带一个同名namespace的,所以这里经常查出来oid=0,不会记录到结果集中
    • 当前【public】在pg_namespace中查询到OID2200记录到oidlist中
  • 【4】给了一个普通的字符串,表示namespace,查pg_namespace,在系统表中确有指定名称,oid记录到结果中

  • 【5】把PG_CATALOG加到最前面

  • 【6】把私有tmp表空间加到最前面

  • 【7】三个全局变量赋值:baseSearchPath就是上面拼接的oidlist、baseCreationNamespace=第一个nsoid

到这里,全局变量的值用namespace_search_path重新计算了一遍。

  • 【8】上面计算完了,真正激活,给activeXXX赋值,其他工具函数只会用active的值。
static void
recomputeNamespacePath(void)
{
  ...

// overrideStack优先级最高,有的话直接使用,不做任何更新
	if (overrideStack)
		return;

//【1】
	if (baseSearchPathValid && namespaceUser == roleid)
		return;

// $2 = 0x28125a0 "\"$user\", public"
	rawname = pstrdup(namespace_search_path);

//【2】
// p (char*)namelist->elements[0]->ptr_value
// $11 = 0x28125a1 "$user"
// p (char*)namelist->elements[1]->ptr_value
// $12 = 0x28125a9 "public"
	SplitIdentifierString(rawname, ',', &namelist)
  ...

  // 解析List的结果集
	oidlist = NIL;
	temp_missing = false;
	foreach(l, namelist)
  /*
    (gdb) p (char*)namelist->elements[0]->ptr_value
    $22 = 0x28125a1 "$user"
    (gdb) p (char*)namelist->elements[1]->ptr_value
    $23 = 0x28125a9 "public"
  */
	{
		char	   *curname = (char *) lfirst(l);
		Oid			namespaceId;

		if (strcmp(curname, "$user") == 0)
		{
			HeapTuple	tuple;

			tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
			if (HeapTupleIsValid(tuple))
			{
				char	   *rname;
        // 查系统表pg_authid,查到了,确实有这个用户
				rname = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname);
        
        // 用户名mingjiegao去pg_namespace里面查不到!
        // namespaceId = 0
				namespaceId = get_namespace_oid(rname, true);
				ReleaseSysCache(tuple);
				// 先做权限检查acl check ...
        // 【3】记录到结果中,注意只记录OID即可
				oidlist = lappend_oid(oidlist, namespaceId);
			}
		}
		else if (strcmp(curname, "pg_temp") == 0)
		{
			if (OidIsValid(myTempNamespace))
			{
				if (!list_member_oid(oidlist, myTempNamespace) &&
					InvokeNamespaceSearchHook(myTempNamespace, false))
					oidlist = lappend_oid(oidlist, myTempNamespace);
			}
			else
			{
				/* If it ought to be the creation namespace, set flag */
				if (oidlist == NIL)
					temp_missing = true;
			}
		}
		else
		{
			// 【4】给了一个普通的字符串,表示namespace,这里查pg_namespace
			namespaceId = get_namespace_oid(curname, true);
			if (OidIsValid(namespaceId) &&
				!list_member_oid(oidlist, namespaceId) &&
				pg_namespace_aclcheck(namespaceId, roleid,
									  ACL_USAGE) == ACLCHECK_OK &&
				InvokeNamespaceSearchHook(namespaceId, false))
        // 在系统表中确有指定名称,oid记录到结果中
				oidlist = lappend_oid(oidlist, namespaceId);
		}
	}

  // 记录第一个解析出来的oid
  // public 2200
	if (oidlist == NIL)
		firstNS = InvalidOid;
	else
		firstNS = linitial_oid(oidlist);
  
  // 【5】把PG_CATALOG加到最前面
  //  (gdb) p oidlist->elements[0]->oid_value
  // $16 = 11
	if (!list_member_oid(oidlist, PG_CATALOG_NAMESPACE))
		oidlist = lcons_oid(PG_CATALOG_NAMESPACE, oidlist);

  // 【6】把私有tmp表空间加到最前面
  // (如果有)
	if (OidIsValid(myTempNamespace) &&
		!list_member_oid(oidlist, myTempNamespace))
		oidlist = lcons_oid(myTempNamespace, oidlist);

  // 如果值都是正确的,不在进行重新赋值,避免值拷贝
	if (baseCreationNamespace == firstNS &&
		baseTempCreationPending == temp_missing &&
		equal(oidlist, baseSearchPath))
	{
		pathChanged = false;
	}
	else
	{
    //【7】三个全局变量赋值:baseSearchPath就是上面拼接的oidlist、baseCreationNamespace=第一个nsoid
		pathChanged = true;

		/* Must save OID list in permanent storage. */
		oldcxt = MemoryContextSwitchTo(TopMemoryContext);
		newpath = list_copy(oidlist);
		MemoryContextSwitchTo(oldcxt);

		/* Now safe to assign to state variables. */
		list_free(baseSearchPath);
		baseSearchPath = newpath;
		baseCreationNamespace = firstNS;
		baseTempCreationPending = temp_missing;
	}

	/* Mark the path valid. */
	baseSearchPathValid = true;
	namespaceUser = roleid;

	//【8】上面计算完了,真正激活使用是在这里。
  // 其他工具函数只会用active的值。
	activeSearchPath = baseSearchPath;
	activeCreationNamespace = baseCreationNamespace;
	activeTempCreationPending = baseTempCreationPending;

	/*
	 * Bump the generation only if something actually changed.  (Notice that
	 * what we compared to was the old state of the base path variables; so
	 * this does not deal with the situation where we have just popped an
	 * override path and restored the prior state of the base path.  Instead
	 * we rely on the override-popping logic to have bumped the generation.)
	 */
	if (pathChanged)
		activePathGeneration++;

	/* Clean up. */
	pfree(rawname);
	list_free(namelist);
	list_free(oidlist);
}

工具函数举例:RangeVarGetCreationNamespace

创建对象时会用到RangeVarGetCreationNamespace

例如我创建表的时候没有指定namespace,那么创建表时就会有这样的参数进入RangeVarGetCreationNamespace:

(gdb) p *newRelation
$20 = {type = T_RangeVar, catalogname = 0x0, schemaname = 0x0, relname = 0x2811ed8 "sss", inh = true, relpersistence = 112 'p', alias = 0x0, location = 13}

执行路径总结起来就是使用activeCreationNamespace的值作为namespaceId返回。

Oid
RangeVarGetCreationNamespace(const RangeVar *newRelation)
{
	Oid			namespaceId;

	/*
	 * We check the catalog name and then ignore it.
	 */
	if (newRelation->catalogname)
	{
		if (strcmp(newRelation->catalogname, get_database_name(MyDatabaseId)) != 0)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
							newRelation->catalogname, newRelation->schemaname,
							newRelation->relname)));
	}

	if (newRelation->schemaname)
	{
...
	}
	else if (newRelation->relpersistence == RELPERSISTENCE_TEMP)
	{
...
	}
	else
	{
		/* use the default creation namespace */
		recomputeNamespacePath();
		if (activeTempCreationPending)
		{
			/* Need to initialize temp namespace */
			AccessTempTableNamespace(true);
			return myTempNamespace;
		}
		namespaceId = activeCreationNamespace;
		if (!OidIsValid(namespaceId))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_SCHEMA),
					 errmsg("no schema has been selected to create in")));
	}

	/* Note: callers will check for CREATE rights when appropriate */

	return namespaceId;
}
  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-05-25 11:38:09  更:2022-05-25 11:39:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 20:03:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码