栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Postgresql源码(48)namespace计算逻辑分析(不显示指定namespace)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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;	

注意:

  • 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)
  
	{
		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 (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;

		
		oldcxt = MemoryContextSwitchTo(TopMemoryContext);
		newpath = list_copy(oidlist);
		MemoryContextSwitchTo(oldcxt);

		
		list_free(baseSearchPath);
		baseSearchPath = newpath;
		baseCreationNamespace = firstNS;
		baseTempCreationPending = temp_missing;
	}

	
	baseSearchPathValid = true;
	namespaceUser = roleid;

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

	
	if (pathChanged)
		activePathGeneration++;

	
	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;

	
	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
	{
		
		recomputeNamespacePath();
		if (activeTempCreationPending)
		{
			
			AccessTempTableNamespace(true);
			return myTempNamespace;
		}
		namespaceId = activeCreationNamespace;
		if (!OidIsValid(namespaceId))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_SCHEMA),
					 errmsg("no schema has been selected to create in")));
	}

	

	return namespaceId;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/926643.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号