默认情况下查询一张表,不指定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大部分逻辑都是在维护、使用三个全局变量:
三个全局变量有两套赋值逻辑:
第一套:计算好baseXXX,然后统一赋值给activeXXX,相当于激活使用base
activeSearchPath = baseSearchPath;
activeCreationNamespace = baseCreationNamespace;
activeTempCreationPending = baseTempCreationPending;
第二套:用临时值给三个变量赋值,相当于临时切换到另一套查找空间,用完了可以切换回第一套
activeSearchPath = entry->searchPath;
activeCreationNamespace = entry->creationNamespace;
activeTempCreationPending = false; /* XXX is this OK? */
注意:
通常使用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
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
recomputeNamespacePath在namespace.c中有20多次调用,下面分析执行流程:
SQL执行中打开任意表文件执行RelnameGetRelid
时,会第一次执行recomputeNamespacePath,后面还会执行多次recomputeNamespacePath但不会重新计算了。
第一次执行总结:
到这里,全局变量的值用namespace_search_path重新计算了一遍。
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
例如我创建表的时候没有指定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;
}