📄 namespace.c
字号:
if (namespaceSearchPathValid && namespaceUser == roleid) return; /* Need a modifiable copy of namespace_search_path string */ rawname = pstrdup(namespace_search_path); /* Parse string into list of identifiers */ if (!SplitIdentifierString(rawname, ',', &namelist)) { /* syntax error in name list */ /* this should not happen if GUC checked check_search_path */ elog(ERROR, "invalid list syntax"); } /* * Convert the list of names to a list of OIDs. If any names are not * recognizable or we don't have read access, just leave them out of the * list. (We can't raise an error, since the search_path setting has * already been accepted.) Don't make duplicate entries, either. */ oidlist = NIL; foreach(l, namelist) { char *curname = (char *) lfirst(l); Oid namespaceId; if (strcmp(curname, "$user") == 0) { /* $user --- substitute namespace matching user name, if any */ HeapTuple tuple; tuple = SearchSysCache(AUTHOID, ObjectIdGetDatum(roleid), 0, 0, 0); if (HeapTupleIsValid(tuple)) { char *rname; rname = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname); namespaceId = GetSysCacheOid(NAMESPACENAME, CStringGetDatum(rname), 0, 0, 0); ReleaseSysCache(tuple); if (OidIsValid(namespaceId) && !list_member_oid(oidlist, namespaceId) && pg_namespace_aclcheck(namespaceId, roleid, ACL_USAGE) == ACLCHECK_OK) oidlist = lappend_oid(oidlist, namespaceId); } } else { /* normal namespace reference */ namespaceId = GetSysCacheOid(NAMESPACENAME, CStringGetDatum(curname), 0, 0, 0); if (OidIsValid(namespaceId) && !list_member_oid(oidlist, namespaceId) && pg_namespace_aclcheck(namespaceId, roleid, ACL_USAGE) == ACLCHECK_OK) oidlist = lappend_oid(oidlist, namespaceId); } } /* * Remember the first member of the explicit list. */ if (oidlist == NIL) firstNS = InvalidOid; else firstNS = linitial_oid(oidlist); /* * Add any implicitly-searched namespaces to the list. Note these go on * the front, not the back; also notice that we do not check USAGE * permissions for these. */ 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 (OidIsValid(mySpecialNamespace) && !list_member_oid(oidlist, mySpecialNamespace)) oidlist = lcons_oid(mySpecialNamespace, oidlist); /* * Now that we've successfully built the new list of namespace OIDs, save * it in permanent storage. */ oldcxt = MemoryContextSwitchTo(TopMemoryContext); newpath = list_copy(oidlist); MemoryContextSwitchTo(oldcxt); /* Now safe to assign to state variable. */ list_free(namespaceSearchPath); namespaceSearchPath = newpath; /* * Update info derived from search path. */ firstExplicitNamespace = firstNS; if (OidIsValid(mySpecialNamespace)) defaultCreationNamespace = mySpecialNamespace; else defaultCreationNamespace = firstNS; /* Mark the path valid. */ namespaceSearchPathValid = true; namespaceUser = roleid; /* Clean up. */ pfree(rawname); list_free(namelist); list_free(oidlist);}/* * InitTempTableNamespace * Initialize temp table namespace on first use in a particular backend */static voidInitTempTableNamespace(void){ char namespaceName[NAMEDATALEN]; Oid namespaceId; /* * First, do permission check to see if we are authorized to make temp * tables. We use a nonstandard error message here since "databasename: * permission denied" might be a tad cryptic. * * Note that ACL_CREATE_TEMP rights are rechecked in pg_namespace_aclmask; * that's necessary since current user ID could change during the session. * But there's no need to make the namespace in the first place until a * temp table creation request is made by someone with appropriate rights. */ if (pg_database_aclcheck(MyDatabaseId, GetUserId(), ACL_CREATE_TEMP) != ACLCHECK_OK) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create temporary tables in database \"%s\"", get_database_name(MyDatabaseId)))); snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId); namespaceId = GetSysCacheOid(NAMESPACENAME, CStringGetDatum(namespaceName), 0, 0, 0); if (!OidIsValid(namespaceId)) { /* * First use of this temp namespace in this database; create it. The * temp namespaces are always owned by the superuser. We leave their * permissions at default --- i.e., no access except to superuser --- * to ensure that unprivileged users can't peek at other backends' * temp tables. This works because the places that access the temp * namespace for my own backend skip permissions checks on it. */ namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); /* Advance command counter to make namespace visible */ CommandCounterIncrement(); } else { /* * If the namespace already exists, clean it out (in case the former * owner crashed without doing so). */ RemoveTempRelations(namespaceId); } /* * Okay, we've prepared the temp namespace ... but it's not committed yet, * so all our work could be undone by transaction rollback. Set flag for * AtEOXact_Namespace to know what to do. */ myTempNamespace = namespaceId; /* It should not be done already. */ AssertState(myTempNamespaceSubID == InvalidSubTransactionId); myTempNamespaceSubID = GetCurrentSubTransactionId(); namespaceSearchPathValid = false; /* need to rebuild list */}/* * End-of-transaction cleanup for namespaces. */voidAtEOXact_Namespace(bool isCommit){ /* * If we abort the transaction in which a temp namespace was selected, * we'll have to do any creation or cleanout work over again. So, just * forget the namespace entirely until next time. On the other hand, if * we commit then register an exit callback to clean out the temp tables * at backend shutdown. (We only want to register the callback once per * session, so this is a good place to do it.) */ if (myTempNamespaceSubID != InvalidSubTransactionId) { if (isCommit) on_shmem_exit(RemoveTempRelationsCallback, 0); else { myTempNamespace = InvalidOid; namespaceSearchPathValid = false; /* need to rebuild list */ } myTempNamespaceSubID = InvalidSubTransactionId; } /* * Clean up if someone failed to do PopSpecialNamespace */ if (OidIsValid(mySpecialNamespace)) { mySpecialNamespace = InvalidOid; namespaceSearchPathValid = false; /* need to rebuild list */ }}/* * AtEOSubXact_Namespace * * At subtransaction commit, propagate the temp-namespace-creation * flag to the parent subtransaction. * * At subtransaction abort, forget the flag if we set it up. */voidAtEOSubXact_Namespace(bool isCommit, SubTransactionId mySubid, SubTransactionId parentSubid){ if (myTempNamespaceSubID == mySubid) { if (isCommit) myTempNamespaceSubID = parentSubid; else { myTempNamespaceSubID = InvalidSubTransactionId; /* TEMP namespace creation failed, so reset state */ myTempNamespace = InvalidOid; namespaceSearchPathValid = false; /* need to rebuild list */ } }}/* * Remove all relations in the specified temp namespace. * * This is called at backend shutdown (if we made any temp relations). * It is also called when we begin using a pre-existing temp namespace, * in order to clean out any relations that might have been created by * a crashed backend. */static voidRemoveTempRelations(Oid tempNamespaceId){ ObjectAddress object; /* * We want to get rid of everything in the target namespace, but not the * namespace itself (deleting it only to recreate it later would be a * waste of cycles). We do this by finding everything that has a * dependency on the namespace. */ object.classId = NamespaceRelationId; object.objectId = tempNamespaceId; object.objectSubId = 0; deleteWhatDependsOn(&object, false);}/* * Callback to remove temp relations at backend exit. */static voidRemoveTempRelationsCallback(int code, Datum arg){ if (OidIsValid(myTempNamespace)) /* should always be true */ { /* Need to ensure we have a usable transaction. */ AbortOutOfAnyTransaction(); StartTransactionCommand(); RemoveTempRelations(myTempNamespace); CommitTransactionCommand(); }}/* * Routines for handling the GUC variable 'search_path'. *//* assign_hook: validate new search_path, do extra actions as needed */const char *assign_search_path(const char *newval, bool doit, GucSource source){ char *rawname; List *namelist; ListCell *l; /* Need a modifiable copy of string */ rawname = pstrdup(newval); /* Parse string into list of identifiers */ if (!SplitIdentifierString(rawname, ',', &namelist)) { /* syntax error in name list */ pfree(rawname); list_free(namelist); return NULL; } /* * If we aren't inside a transaction, we cannot do database access so * cannot verify the individual names. Must accept the list on faith. */ if (source >= PGC_S_INTERACTIVE && IsTransactionState()) { /* * Verify that all the names are either valid namespace names or * "$user". We do not require $user to correspond to a valid * namespace. We do not check for USAGE rights, either; should we? * * When source == PGC_S_TEST, we are checking the argument of an ALTER * DATABASE SET or ALTER USER SET command. It could be that the * intended use of the search path is for some other database, so we * should not error out if it mentions schemas not present in the * current database. We reduce the message to NOTICE instead. */ foreach(l, namelist) { char *curname = (char *) lfirst(l); if (strcmp(curname, "$user") == 0) continue; if (!SearchSysCacheExists(NAMESPACENAME, CStringGetDatum(curname), 0, 0, 0)) ereport((source == PGC_S_TEST) ? NOTICE : ERROR, (errcode(ERRCODE_UNDEFINED_SCHEMA), errmsg("schema \"%s\" does not exist", curname))); } } pfree(rawname); list_free(namelist); /* * We mark the path as needing recomputation, but don't do anything until * it's needed. This avoids trying to do database access during GUC * initialization. */ if (doit) namespaceSearchPathValid = false; return newval;}/* * InitializeSearchPath: initialize module during InitPostgres. * * This is called after we are up enough to be able to do catalog lookups. */voidInitializeSearchPath(void){ if (IsBootstrapProcessingMode()) { /* * In bootstrap mode, the search path must be 'pg_catalog' so that * tables are created in the proper namespace; ignore the GUC setting. */ MemoryContext oldcxt; oldcxt = MemoryContextSwitchTo(TopMemoryContext); namespaceSearchPath = list_make1_oid(PG_CATALOG_NAMESPACE); MemoryContextSwitchTo(oldcxt); defaultCreationNamespace = PG_CATALOG_NAMESPACE; firstExplicitNamespace = PG_CATALOG_NAMESPACE; namespaceSearchPathValid = true; namespaceUser = GetUserId(); } else { /* * In normal mode, arrange for a callback on any syscache invalidation * of pg_namespace rows. */ CacheRegisterSyscacheCallback(NAMESPACEOID, NamespaceCallback, (Datum) 0); /* Force search path to be recomputed on next use */ namespaceSearchPathValid = false; }}/* * NamespaceCallback * Syscache inval callback function */static voidNamespaceCallback(Datum arg, Oid relid){ /* Force search path to be recomputed on next use */ namespaceSearchPathValid = false;}/* * Fetch the active search path. The return value is a palloc'ed list * of OIDs; the caller is responsible for freeing this storage as * appropriate. * * The returned list includes the implicitly-prepended namespaces only if * includeImplicit is true. */List *fetch_search_path(bool includeImplicit){ List *result; recomputeNamespacePath(); result = list_copy(namespaceSearchPath); if (!includeImplicit) { while (result && linitial_oid(result) != firstExplicitNamespace) result = list_delete_first(result); } return result;}/* * Export the FooIsVisible functions as SQL-callable functions. */Datumpg_table_is_visible(PG_FUNCTION_ARGS){ Oid oid = PG_GETARG_OID(0); PG_RETURN_BOOL(RelationIsVisible(oid));}Datumpg_type_is_visible(PG_FUNCTION_ARGS){ Oid oid = PG_GETARG_OID(0); PG_RETURN_BOOL(TypeIsVisible(oid));}Datumpg_function_is_visible(PG_FUNCTION_ARGS){ Oid oid = PG_GETARG_OID(0); PG_RETURN_BOOL(FunctionIsVisible(oid));}Datumpg_operator_is_visible(PG_FUNCTION_ARGS){ Oid oid = PG_GETARG_OID(0); PG_RETURN_BOOL(OperatorIsVisible(oid));}Datumpg_opclass_is_visible(PG_FUNCTION_ARGS){ Oid oid = PG_GETARG_OID(0); PG_RETURN_BOOL(OpclassIsVisible(oid));}Datumpg_conversion_is_visible(PG_FUNCTION_ARGS){ Oid oid = PG_GETARG_OID(0); PG_RETURN_BOOL(ConversionIsVisible(oid));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -