搜索优先级
默认情况下查询一张表,不指定namespace,系统查询的是哪个namespace?
按"$user", public 场景来说,搜索顺序:
1、临时表 2、系统表 3、普通表
(原因见下面recomputeNamespacePath函数分析)
postgres=
search_path
"$user", public
pg_temp_3.pg_class
pg_catalog.pg_class
public.pg_class
namespace核心全局变量
1 规律
namespace大部分逻辑都是在维护、使用三个全局变量:
- 搜索空间:activeSearchPath
- 创建空间:activeCreationNamespace
- 临时空间: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失效
- 什么时候重新算?
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)
{
...
if (overrideStack)
return;
if (baseSearchPathValid && namespaceUser == roleid)
return;
rawname = pstrdup(namespace_search_path);
SplitIdentifierString(rawname, ',', &namelist)
...
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;
rname = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname);
namespaceId = get_namespace_oid(rname, true);
ReleaseSysCache(tuple);
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
{
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))
oidlist = lappend_oid(oidlist, namespaceId);
}
}
if (oidlist == NIL)
firstNS = InvalidOid;
else
firstNS = linitial_oid(oidlist);
if (!list_member_oid(oidlist, PG_CATALOG_NAMESPACE))
oidlist = lcons_oid(PG_CATALOG_NAMESPACE, oidlist);
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
{
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;
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;
}
|