Impressum namespace.c
Interaktion und Portierbarkeitunbekannt
/*-------------------------------------------------------------------------
*
* namespace . c
* code to support accessing and searching namespaces
*
* This is separate from pg_namespace . c , which contains the routines that
* directly manipulate the pg_namespace system catalog . This module
* provides routines associated with defining a " namespace search path "
* and implementing search - path - controlled searches .
*
*
* Portions Copyright ( c ) 1996 - 2025 , PostgreSQL Global Development Group
* Portions Copyright ( c ) 1994 , Regents of the University of California
*
* IDENTIFICATION
* src / backend / catalog / namespace . c
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
#include "postgres.h"
#include "access/htup_details.h"
#include "access/parallel.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "catalog/dependency.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_ts_config.h"
#include "catalog/pg_ts_dict.h"
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "common/hashfn_unstable.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "storage/procarray.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
#include "utils/guc_hooks.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/varlena.h"
/*
* The namespace search path is a possibly - empty list of namespace OIDs .
* In addition to the explicit list , implicitly - searched namespaces
* may be included :
*
* 1 . If a TEMP table namespace has been initialized in this session , it
* is implicitly searched first .
*
* 2 . The system catalog namespace is always searched . If the system
* namespace is present in the explicit path then it will be searched in
* the specified order ; otherwise it will be searched after TEMP tables and
* * before * the explicit list . ( It might seem that the system namespace
* should be implicitly last , but this behavior appears to be required by
* SQL99 . Also , this provides a way to search the system namespace first
* without thereby making it the default creation target namespace . )
*
* For security reasons , searches using the search path will ignore the temp
* namespace when searching for any object type other than relations and
* types . ( We must allow types since temp tables have rowtypes . )
*
* The default creation target namespace is always the first element of the
* explicit list . If the explicit list is empty , there is no default target .
*
* The textual specification of search_path can include " $ user " to refer to
* the namespace named the same as the current user , if any . ( This is just
* ignored if there is no such namespace . ) Also , it can include " pg_temp "
* to refer to the current backend ' s temp namespace . This is usually also
* ignorable if the temp namespace hasn ' t been set up , but there ' s a special
* case : if " pg_temp " appears first then it should be the default creation
* target . We kluge this case a little bit so that the temp namespace isn ' t
* set up until the first attempt to create something in it . ( The reason for
* klugery is that we can ' t create the temp namespace outside a transaction ,
* but initial GUC processing of search_path happens outside a transaction . )
* activeTempCreationPending is true if " pg_temp " appears first in the string
* but is not reflected in activeCreationNamespace because the namespace isn ' t
* set up yet .
*
* In bootstrap mode , the search path is set equal to " pg_catalog " , so that
* the system namespace is the only one searched or inserted into .
* initdb is also careful to set search_path to " pg_catalog " for its
* post - bootstrap standalone backend runs . Otherwise the default search
* path is determined by GUC . The factory default path contains the PUBLIC
* namespace ( if it exists ) , preceded by the user ' s personal namespace
* ( if one exists ) .
*
* activeSearchPath is always the actually active path ; it points to
* baseSearchPath which is the list derived from namespace_search_path .
*
* If baseSearchPathValid is false , then baseSearchPath ( and other derived
* variables ) need to be recomputed from namespace_search_path , or retrieved
* from the search path cache if there haven ' t been any syscache
* invalidations . We mark it invalid upon an assignment to
* namespace_search_path or receipt of a syscache invalidation event for
* pg_namespace or pg_authid . The recomputation is done during the next
* lookup attempt .
*
* Any namespaces mentioned in namespace_search_path that are not readable
* by the current user ID are simply left out of baseSearchPath ; so
* we have to be willing to recompute the path when current userid changes .
* namespaceUser is the userid the path has been computed for .
*
* Note : all data pointed to by these List variables is in TopMemoryContext .
*
* activePathGeneration is incremented whenever the effective values of
* activeSearchPath / activeCreationNamespace / activeTempCreationPending change .
* This can be used to quickly detect whether any change has happened since
* a previous examination of the search path state .
*/
/* These variables define the actually active state: */
static List *activeSearchPath = NIL;
/* default place to create stuff; if InvalidOid, no default */
static Oid activeCreationNamespace = InvalidOid;
/* if true, activeCreationNamespace is wrong, it should be temp namespace */
static bool activeTempCreationPending = false ;
/* current generation counter; make sure this is never zero */
static uint64 activePathGeneration = 1 ;
/* These variables are the values last derived from namespace_search_path: */
static List *baseSearchPath = NIL;
static Oid baseCreationNamespace = InvalidOid;
static bool baseTempCreationPending = false ;
static Oid namespaceUser = InvalidOid;
/* The above four values are valid only if baseSearchPathValid */
static bool baseSearchPathValid = true ;
/*
* Storage for search path cache . Clear searchPathCacheValid as a simple
* way to invalidate * all * the cache entries , not just the active one .
*/
static bool searchPathCacheValid = false ;
static MemoryContext SearchPathCacheContext = NULL;
typedef struct SearchPathCacheKey
{
const char *searchPath;
Oid roleid;
} SearchPathCacheKey;
typedef struct SearchPathCacheEntry
{
SearchPathCacheKey key;
List *oidlist; /* namespace OIDs that pass ACL checks */
List *finalPath; /* cached final computed search path */
Oid firstNS; /* first explicitly-listed namespace */
bool temp_missing;
bool forceRecompute; /* force recompute of finalPath */
/* needed for simplehash */
char status;
} SearchPathCacheEntry;
/*
* myTempNamespace is InvalidOid until and unless a TEMP namespace is set up
* in a particular backend session ( this happens when a CREATE TEMP TABLE
* command is first executed ) . Thereafter it ' s the OID of the temp namespace .
*
* myTempToastNamespace is the OID of the namespace for my temp tables ' toast
* tables . It is set when myTempNamespace is , and is InvalidOid before that .
*
* myTempNamespaceSubID shows whether we ' ve created the TEMP namespace in the
* current subtransaction . The flag propagates up the subtransaction tree ,
* so the main transaction will correctly recognize the flag if all
* intermediate subtransactions commit . When it is InvalidSubTransactionId ,
* we either haven ' t made the TEMP namespace yet , or have successfully
* committed its creation , depending on whether myTempNamespace is valid .
*/
static Oid myTempNamespace = InvalidOid;
static Oid myTempToastNamespace = InvalidOid;
static SubTransactionId myTempNamespaceSubID = InvalidSubTransactionId;
/*
* This is the user ' s textual search path specification - - - it ' s the value
* of the GUC variable ' search_path ' .
*/
char *namespace_search_path = NULL;
/* Local functions */
static bool RelationIsVisibleExt(Oid relid, bool *is_missing);
static bool TypeIsVisibleExt(Oid typid, bool *is_missing);
static bool FunctionIsVisibleExt(Oid funcid, bool *is_missing);
static bool OperatorIsVisibleExt(Oid oprid, bool *is_missing);
static bool OpclassIsVisibleExt(Oid opcid, bool *is_missing);
static bool OpfamilyIsVisibleExt(Oid opfid, bool *is_missing);
static bool CollationIsVisibleExt(Oid collid, bool *is_missing);
static bool ConversionIsVisibleExt(Oid conid, bool *is_missing);
static bool StatisticsObjIsVisibleExt(Oid stxid, bool *is_missing);
static bool TSParserIsVisibleExt(Oid prsId, bool *is_missing);
static bool TSDictionaryIsVisibleExt(Oid dictId, bool *is_missing);
static bool TSTemplateIsVisibleExt(Oid tmplId, bool *is_missing);
static bool TSConfigIsVisibleExt(Oid cfgid, bool *is_missing);
static void recomputeNamespacePath(void );
static void AccessTempTableNamespace(bool force);
static void InitTempTableNamespace(void );
static void RemoveTempRelations(Oid tempNamespaceId);
static void RemoveTempRelationsCallback(int code, Datum arg);
static void InvalidationCallback(Datum arg, int cacheid, uint32 hashvalue);
static bool MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
bool include_out_arguments, int pronargs,
int **argnumbers);
/*
* Recomputing the namespace path can be costly when done frequently , such as
* when a function has search_path set in proconfig . Add a search path cache
* that can be used by recomputeNamespacePath ( ) .
*
* The cache is also used to remember already - validated strings in
* check_search_path ( ) to avoid the need to call SplitIdentifierString ( )
* repeatedly .
*
* The search path cache is based on a wrapper around a simplehash hash table
* ( nsphash , defined below ) . The spcache wrapper deals with OOM while trying
* to initialize a key , optimizes repeated lookups of the same key , and also
* offers a more convenient API .
*/
static inline uint32
spcachekey_hash(SearchPathCacheKey key)
{
fasthash_state hs;
int sp_len;
fasthash_init(&hs, 0 );
hs.accum = key.roleid;
fasthash_combine(&hs);
/*
* Combine search path into the hash and save the length for tweaking the
* final mix .
*/
sp_len = fasthash_accum_cstring(&hs, key.searchPath);
return fasthash_final32(&hs, sp_len);
}
static inline bool
spcachekey_equal(SearchPathCacheKey a, SearchPathCacheKey b)
{
return a.roleid == b.roleid &&
strcmp(a.searchPath, b.searchPath) == 0 ;
}
#define SH_PREFIX nsphash
#define SH_ELEMENT_TYPE SearchPathCacheEntry
#define SH_KEY_TYPE SearchPathCacheKey
#define SH_KEY key
#define SH_HASH_KEY(tb, key) spcachekey_hash(key)
#define SH_EQUAL(tb, a, b) spcachekey_equal(a, b)
#define SH_SCOPE static inline
#define SH_DECLARE
#define SH_DEFINE
#include "lib/simplehash.h"
/*
* We only expect a small number of unique search_path strings to be used . If
* this cache grows to an unreasonable size , reset it to avoid steady - state
* memory growth . Most likely , only a few of those entries will benefit from
* the cache , and the cache will be quickly repopulated with such entries .
*/
#define SPCACHE_RESET_THRESHOLD 256
static nsphash_hash *SearchPathCache = NULL;
static SearchPathCacheEntry *LastSearchPathCacheEntry = NULL;
/*
* Create or reset search_path cache as necessary .
*/
static void
spcache_init(void )
{
if (SearchPathCache && searchPathCacheValid &&
SearchPathCache->members < SPCACHE_RESET_THRESHOLD)
return ;
searchPathCacheValid = false ;
baseSearchPathValid = false ;
/*
* Make sure we don ' t leave dangling pointers if a failure happens during
* initialization .
*/
SearchPathCache = NULL;
LastSearchPathCacheEntry = NULL;
if (SearchPathCacheContext == NULL)
{
/* Make the context we'll keep search path cache hashtable in */
SearchPathCacheContext = AllocSetContextCreate(TopMemoryContext,
"search_path processing cache" ,
ALLOCSET_DEFAULT_SIZES);
}
else
{
MemoryContextReset(SearchPathCacheContext);
}
/* arbitrary initial starting size of 16 elements */
SearchPathCache = nsphash_create(SearchPathCacheContext, 16 , NULL);
searchPathCacheValid = true ;
}
/*
* Look up entry in search path cache without inserting . Returns NULL if not
* present .
*/
static SearchPathCacheEntry *
spcache_lookup(const char *searchPath, Oid roleid)
{
if (LastSearchPathCacheEntry &&
LastSearchPathCacheEntry->key.roleid == roleid &&
strcmp(LastSearchPathCacheEntry->key.searchPath, searchPath) == 0 )
{
return LastSearchPathCacheEntry;
}
else
{
SearchPathCacheEntry *entry;
SearchPathCacheKey cachekey = {
.searchPath = searchPath,
.roleid = roleid
};
entry = nsphash_lookup(SearchPathCache, cachekey);
if (entry)
LastSearchPathCacheEntry = entry;
return entry;
}
}
/*
* Look up or insert entry in search path cache .
*
* Initialize key safely , so that OOM does not leave an entry without a valid
* key . Caller must ensure that non - key contents are properly initialized .
*/
static SearchPathCacheEntry *
spcache_insert(const char *searchPath, Oid roleid)
{
if (LastSearchPathCacheEntry &&
LastSearchPathCacheEntry->key.roleid == roleid &&
strcmp(LastSearchPathCacheEntry->key.searchPath, searchPath) == 0 )
{
return LastSearchPathCacheEntry;
}
else
{
SearchPathCacheEntry *entry;
SearchPathCacheKey cachekey = {
.searchPath = searchPath,
.roleid = roleid
};
/*
* searchPath is not saved in SearchPathCacheContext . First perform a
* lookup , and copy searchPath only if we need to create a new entry .
*/
entry = nsphash_lookup(SearchPathCache, cachekey);
if (!entry)
{
bool found;
cachekey.searchPath = MemoryContextStrdup(SearchPathCacheContext, searchPath);
entry = nsphash_insert(SearchPathCache, cachekey, &found);
Assert(!found);
entry->oidlist = NIL;
entry->finalPath = NIL;
entry->firstNS = InvalidOid;
entry->temp_missing = false ;
entry->forceRecompute = false ;
/* do not touch entry->status, used by simplehash */
}
LastSearchPathCacheEntry = entry;
return entry;
}
}
/*
* RangeVarGetRelidExtended
* Given a RangeVar describing an existing relation ,
* select the proper namespace and look up the relation OID .
*
* If the schema or relation is not found , return InvalidOid if flags contains
* RVR_MISSING_OK , otherwise raise an error .
*
* If flags contains RVR_NOWAIT , throw an error if we ' d have to wait for a
* lock .
*
* If flags contains RVR_SKIP_LOCKED , return InvalidOid if we ' d have to wait
* for a lock .
*
* flags cannot contain both RVR_NOWAIT and RVR_SKIP_LOCKED .
*
* Note that if RVR_MISSING_OK and RVR_SKIP_LOCKED are both specified , a
* return value of InvalidOid could either mean the relation is missing or it
* could not be locked .
*
* Callback allows caller to check permissions or acquire additional locks
* prior to grabbing the relation lock .
*/
Oid
RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode,
uint32 flags,
RangeVarGetRelidCallback callback, void *callback_arg)
{
uint64 inval_count;
Oid relId;
Oid oldRelId = InvalidOid;
bool retry = false ;
bool missing_ok = (flags & RVR_MISSING_OK) != 0 ;
/* verify that flags do no conflict */
Assert(!((flags & RVR_NOWAIT) && (flags & RVR_SKIP_LOCKED)));
/*
* We check the catalog name and then ignore it .
*/
if (relation->catalogname)
{
if (strcmp(relation->catalogname, get_database_name(MyDatabaseId)) != 0 )
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cross-database references are not implemented: \" %s.%s.%s\"" ,
relation->catalogname, relation->schemaname,
relation->relname)));
}
/*
* DDL operations can change the results of a name lookup . Since all such
* operations will generate invalidation messages , we keep track of
* whether any such messages show up while we ' re performing the operation ,
* and retry until either ( 1 ) no more invalidation messages show up or ( 2 )
* the answer doesn ' t change .
*
* But if lockmode = NoLock , then we assume that either the caller is OK
* with the answer changing under them , or that they already hold some
* appropriate lock , and therefore return the first answer we get without
* checking for invalidation messages . Also , if the requested lock is
* already held , LockRelationOid will not AcceptInvalidationMessages , so
* we may fail to notice a change . We could protect against that case by
* calling AcceptInvalidationMessages ( ) before beginning this loop , but
* that would add a significant amount overhead , so for now we don ' t .
*/
for (;;)
{
/*
* Remember this value , so that , after looking up the relation name
* and locking its OID , we can check whether any invalidation messages
* have been processed that might require a do - over .
*/
inval_count = SharedInvalidMessageCounter;
/*
* Some non - default relpersistence value may have been specified . The
* parser never generates such a RangeVar in simple DML , but it can
* happen in contexts such as " CREATE TEMP TABLE foo ( f1 int PRIMARY
* KEY ) " . Such a command will generate an added CREATE INDEX
* operation , which must be careful to find the temp table , even when
* pg_temp is not first in the search path .
*/
if (relation->relpersistence == RELPERSISTENCE_TEMP)
{
if (!OidIsValid(myTempNamespace))
relId = InvalidOid; /* this probably can't happen? */
else
{
if (relation->schemaname)
{
Oid namespaceId;
namespaceId = LookupExplicitNamespace(relation->schemaname, missing_ok);
/*
* For missing_ok , allow a non - existent schema name to
* return InvalidOid .
*/
if (namespaceId != myTempNamespace)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("temporary tables cannot specify a schema name" )));
}
relId = get_relname_relid(relation->relname, myTempNamespace);
}
}
else if (relation->schemaname)
{
Oid namespaceId;
/* use exact schema given */
namespaceId = LookupExplicitNamespace(relation->schemaname, missing_ok);
if (missing_ok && !OidIsValid(namespaceId))
relId = InvalidOid;
else
relId = get_relname_relid(relation->relname, namespaceId);
}
else
{
/* search the namespace path */
relId = RelnameGetRelid(relation->relname);
}
/*
* Invoke caller - supplied callback , if any .
*
* This callback is a good place to check permissions : we haven ' t
* taken the table lock yet ( and it ' s really best to check permissions
* before locking anything ! ) , but we ' ve gotten far enough to know what
* OID we think we should lock . Of course , concurrent DDL might
* change things while we ' re waiting for the lock , but in that case
* the callback will be invoked again for the new OID .
*/
if (callback)
callback(relation, relId, oldRelId, callback_arg);
/*
* If no lock requested , we assume the caller knows what they ' re
* doing . They should have already acquired a heavyweight lock on
* this relation earlier in the processing of this same statement , so
* it wouldn ' t be appropriate to AcceptInvalidationMessages ( ) here , as
* that might pull the rug out from under them .
*/
if (lockmode == NoLock)
break ;
/*
* If , upon retry , we get back the same OID we did last time , then the
* invalidation messages we processed did not change the final answer .
* So we ' re done .
*
* If we got a different OID , we ' ve locked the relation that used to
* have this name rather than the one that does now . So release the
* lock .
*/
if (retry)
{
if (relId == oldRelId)
break ;
if (OidIsValid(oldRelId))
UnlockRelationOid(oldRelId, lockmode);
}
/*
* Lock relation . This will also accept any pending invalidation
* messages . If we got back InvalidOid , indicating not found , then
* there ' s nothing to lock , but we accept invalidation messages
* anyway , to flush any negative catcache entries that may be
* lingering .
*/
if (!OidIsValid(relId))
AcceptInvalidationMessages();
else if (!(flags & (RVR_NOWAIT | RVR_SKIP_LOCKED)))
LockRelationOid(relId, lockmode);
else if (!ConditionalLockRelationOid(relId, lockmode))
{
int elevel = (flags & RVR_SKIP_LOCKED) ? DEBUG1 : ERROR;
if (relation->schemaname)
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("could not obtain lock on relation \" %s.%s\"" ,
relation->schemaname, relation->relname)));
else
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("could not obtain lock on relation \" %s\"" ,
relation->relname)));
return InvalidOid;
}
/*
* If no invalidation message were processed , we ' re done !
*/
if (inval_count == SharedInvalidMessageCounter)
break ;
/*
* Something may have changed . Let ' s repeat the name lookup , to make
* sure this name still references the same relation it did
* previously .
*/
retry = true ;
oldRelId = relId;
}
if (!OidIsValid(relId))
{
int elevel = missing_ok ? DEBUG1 : ERROR;
if (relation->schemaname)
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("relation \" %s.%s\" does not exist" ,
relation->schemaname, relation->relname)));
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("relation \" %s\" does not exist" ,
relation->relname)));
}
return relId;
}
/*
* RangeVarGetCreationNamespace
* Given a RangeVar describing a to - be - created relation ,
* choose which namespace to create it in .
*
* Note : calling this may result in a CommandCounterIncrement operation .
* That will happen on the first request for a temp table in any particular
* backend run ; we will need to either create or clean out the temp schema .
*/
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)
{
/* check for pg_temp alias */
if (strcmp(newRelation->schemaname, "pg_temp" ) == 0 )
{
/* Initialize temp namespace */
AccessTempTableNamespace(false );
return myTempNamespace;
}
/* use exact schema given */
namespaceId = get_namespace_oid(newRelation->schemaname, false );
/* we do not check for USAGE rights here! */
}
else if (newRelation->relpersistence == RELPERSISTENCE_TEMP)
{
/* Initialize temp namespace */
AccessTempTableNamespace(false );
return myTempNamespace;
}
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;
}
/*
* RangeVarGetAndCheckCreationNamespace
*
* This function returns the OID of the namespace in which a new relation
* with a given name should be created . If the user does not have CREATE
* permission on the target namespace , this function will instead signal
* an ERROR .
*
* If non - NULL , * existing_relation_id is set to the OID of any existing relation
* with the same name which already exists in that namespace , or to InvalidOid
* if no such relation exists .
*
* If lockmode ! = NoLock , the specified lock mode is acquired on the existing
* relation , if any , provided that the current user owns the target relation .
* However , if lockmode ! = NoLock and the user does not own the target
* relation , we throw an ERROR , as we must not try to lock relations the
* user does not have permissions on .
*
* As a side effect , this function acquires AccessShareLock on the target
* namespace . Without this , the namespace could be dropped before our
* transaction commits , leaving behind relations with relnamespace pointing
* to a no - longer - existent namespace .
*
* As a further side - effect , if the selected namespace is a temporary namespace ,
* we mark the RangeVar as RELPERSISTENCE_TEMP .
*/
Oid
RangeVarGetAndCheckCreationNamespace(RangeVar *relation,
LOCKMODE lockmode,
Oid *existing_relation_id)
{
uint64 inval_count;
Oid relid;
Oid oldrelid = InvalidOid;
Oid nspid;
Oid oldnspid = InvalidOid;
bool retry = false ;
/*
* We check the catalog name and then ignore it .
*/
if (relation->catalogname)
{
if (strcmp(relation->catalogname, get_database_name(MyDatabaseId)) != 0 )
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cross-database references are not implemented: \" %s.%s.%s\"" ,
relation->catalogname, relation->schemaname,
relation->relname)));
}
/*
* As in RangeVarGetRelidExtended ( ) , we guard against concurrent DDL
* operations by tracking whether any invalidation messages are processed
* while we ' re doing the name lookups and acquiring locks . See comments
* in that function for a more detailed explanation of this logic .
*/
for (;;)
{
AclResult aclresult;
inval_count = SharedInvalidMessageCounter;
/* Look up creation namespace and check for existing relation. */
nspid = RangeVarGetCreationNamespace(relation);
Assert(OidIsValid(nspid));
if (existing_relation_id != NULL)
relid = get_relname_relid(relation->relname, nspid);
else
relid = InvalidOid;
/*
* In bootstrap processing mode , we don ' t bother with permissions or
* locking . Permissions might not be working yet , and locking is
* unnecessary .
*/
if (IsBootstrapProcessingMode())
break ;
/* Check namespace permissions. */
aclresult = object_aclcheck(NamespaceRelationId, nspid, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
get_namespace_name(nspid));
if (retry)
{
/* If nothing changed, we're done. */
if (relid == oldrelid && nspid == oldnspid)
break ;
/* If creation namespace has changed, give up old lock. */
if (nspid != oldnspid)
UnlockDatabaseObject(NamespaceRelationId, oldnspid, 0 ,
AccessShareLock);
/* If name points to something different, give up old lock. */
if (relid != oldrelid && OidIsValid(oldrelid) && lockmode != NoLock)
UnlockRelationOid(oldrelid, lockmode);
}
/* Lock namespace. */
if (nspid != oldnspid)
LockDatabaseObject(NamespaceRelationId, nspid, 0 , AccessShareLock);
/* Lock relation, if required if and we have permission. */
if (lockmode != NoLock && OidIsValid(relid))
{
if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)),
relation->relname);
if (relid != oldrelid)
LockRelationOid(relid, lockmode);
}
/* If no invalidation message were processed, we're done! */
if (inval_count == SharedInvalidMessageCounter)
break ;
/* Something may have changed, so recheck our work. */
retry = true ;
oldrelid = relid;
oldnspid = nspid;
}
RangeVarAdjustRelationPersistence(relation, nspid);
if (existing_relation_id != NULL)
*existing_relation_id = relid;
return nspid;
}
/*
* Adjust the relpersistence for an about - to - be - created relation based on the
* creation namespace , and throw an error for invalid combinations .
*/
void
RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid)
{
switch (newRelation->relpersistence)
{
case RELPERSISTENCE_TEMP:
if (!isTempOrTempToastNamespace(nspid))
{
if (isAnyTempNamespace(nspid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("cannot create relations in temporary schemas of other sessions" )));
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("cannot create temporary relation in non-temporary schema" )));
}
break ;
case RELPERSISTENCE_PERMANENT:
if (isTempOrTempToastNamespace(nspid))
newRelation->relpersistence = RELPERSISTENCE_TEMP;
else if (isAnyTempNamespace(nspid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("cannot create relations in temporary schemas of other sessions" )));
break ;
default :
if (isAnyTempNamespace(nspid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("only temporary relations may be created in temporary schemas" )));
}
}
/*
* RelnameGetRelid
* Try to resolve an unqualified relation name .
* Returns OID if relation found in search path , else InvalidOid .
*/
Oid
RelnameGetRelid(const char *relname)
{
Oid relid;
ListCell *l;
recomputeNamespacePath();
foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
relid = get_relname_relid(relname, namespaceId);
if (OidIsValid(relid))
return relid;
}
/* Not found in path */
return InvalidOid;
}
/*
* RelationIsVisible
* Determine whether a relation ( identified by OID ) is visible in the
* current search path . Visible means " would be found by searching
* for the unqualified relation name " .
*/
bool
RelationIsVisible(Oid relid)
{
return RelationIsVisibleExt(relid, NULL);
}
/*
* RelationIsVisibleExt
* As above , but if the relation isn ' t found and is_missing is not NULL ,
* then set * is_missing = true and return false instead of throwing
* an error . ( Caller must initialize * is_missing = false . )
*/
static bool
RelationIsVisibleExt(Oid relid, bool *is_missing)
{
HeapTuple reltup;
Form_pg_class relform;
Oid relnamespace;
bool visible;
reltup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(reltup))
{
if (is_missing != NULL)
{
*is_missing = true ;
return false ;
}
elog(ERROR, "cache lookup failed for relation %u" , relid);
}
relform = (Form_pg_class) GETSTRUCT(reltup);
recomputeNamespacePath();
/*
* Quick check : if it ain ' t in the path at all , it ain ' t visible . Items in
* the system namespace are surely in the path and so we needn ' t even do
* list_member_oid ( ) for them .
*/
relnamespace = relform->relnamespace;
if (relnamespace != PG_CATALOG_NAMESPACE &&
!list_member_oid(activeSearchPath, relnamespace))
visible = false ;
else
{
/*
* If it is in the path , it might still not be visible ; it could be
* hidden by another relation of the same name earlier in the path . So
* we must do a slow check for conflicting relations .
*/
char *relname = NameStr(relform->relname);
ListCell *l;
visible = false ;
foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
if (namespaceId == relnamespace)
{
/* Found it first in path */
visible = true ;
break ;
}
if (OidIsValid(get_relname_relid(relname, namespaceId)))
{
/* Found something else first in path */
break ;
}
}
}
ReleaseSysCache(reltup);
return visible;
}
/*
* TypenameGetTypid
* Wrapper for binary compatibility .
*/
Oid
TypenameGetTypid(const char *typname)
{
return TypenameGetTypidExtended(typname, true );
}
/*
* TypenameGetTypidExtended
* Try to resolve an unqualified datatype name .
* Returns OID if type found in search path , else InvalidOid .
*
* This is essentially the same as RelnameGetRelid .
*/
Oid
TypenameGetTypidExtended(const char *typname, bool temp_ok)
{
Oid typid;
ListCell *l;
recomputeNamespacePath();
foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
if (!temp_ok && namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
typid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
PointerGetDatum(typname),
ObjectIdGetDatum(namespaceId));
if (OidIsValid(typid))
return typid;
}
/* Not found in path */
return InvalidOid;
}
/*
* TypeIsVisible
* Determine whether a type ( identified by OID ) is visible in the
* current search path . Visible means " would be found by searching
* for the unqualified type name " .
*/
bool
TypeIsVisible(Oid typid)
{
return TypeIsVisibleExt(typid, NULL);
}
/*
* TypeIsVisibleExt
* As above , but if the type isn ' t found and is_missing is not NULL ,
* then set * is_missing = true and return false instead of throwing
* an error . ( Caller must initialize * is_missing = false . )
*/
static bool
TypeIsVisibleExt(Oid typid, bool *is_missing)
{
HeapTuple typtup;
Form_pg_type typform;
Oid typnamespace;
bool visible;
typtup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(typtup))
{
if (is_missing != NULL)
{
*is_missing = true ;
return false ;
}
elog(ERROR, "cache lookup failed for type %u" , typid);
}
typform = (Form_pg_type) GETSTRUCT(typtup);
recomputeNamespacePath();
/*
* Quick check : if it ain ' t in the path at all , it ain ' t visible . Items in
* the system namespace are surely in the path and so we needn ' t even do
* list_member_oid ( ) for them .
*/
typnamespace = typform->typnamespace;
if (typnamespace != PG_CATALOG_NAMESPACE &&
!list_member_oid(activeSearchPath, typnamespace))
visible = false ;
else
{
/*
* If it is in the path , it might still not be visible ; it could be
* hidden by another type of the same name earlier in the path . So we
* must do a slow check for conflicting types .
*/
char *typname = NameStr(typform->typname);
ListCell *l;
visible = false ;
foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
if (namespaceId == typnamespace)
{
/* Found it first in path */
visible = true ;
break ;
}
if (SearchSysCacheExists2(TYPENAMENSP,
PointerGetDatum(typname),
ObjectIdGetDatum(namespaceId)))
{
/* Found something else first in path */
break ;
}
}
}
ReleaseSysCache(typtup);
return visible;
}
/*
* FuncnameGetCandidates
* Given a possibly - qualified function name and argument count ,
* retrieve a list of the possible matches .
*
* If nargs is - 1 , we return all functions matching the given name ,
* regardless of argument count . ( argnames must be NIL , and expand_variadic
* and expand_defaults must be false , in this case . )
*
* If argnames isn ' t NIL , we are considering a named - or mixed - notation call ,
* and only functions having all the listed argument names will be returned .
* ( We assume that length ( argnames ) < = nargs and all the passed - in names are
* distinct . ) The returned structs will include an argnumbers array showing
* the actual argument index for each logical argument position .
*
* If expand_variadic is true , then variadic functions having the same number
* or fewer arguments will be retrieved , with the variadic argument and any
* additional argument positions filled with the variadic element type .
* nvargs in the returned struct is set to the number of such arguments .
* If expand_variadic is false , variadic arguments are not treated specially ,
* and the returned nvargs will always be zero .
*
* If expand_defaults is true , functions that could match after insertion of
* default argument values will also be retrieved . In this case the returned
* structs could have nargs > passed - in nargs , and ndargs is set to the number
* of additional args ( which can be retrieved from the function ' s
* proargdefaults entry ) .
*
* If include_out_arguments is true , then OUT - mode arguments are considered to
* be included in the argument list . Their types are included in the returned
* arrays , and argnumbers are indexes in proallargtypes not proargtypes .
* We also set nominalnargs to be the length of proallargtypes not proargtypes .
* Otherwise OUT - mode arguments are ignored .
*
* It is not possible for nvargs and ndargs to both be nonzero in the same
* list entry , since default insertion allows matches to functions with more
* than nargs arguments while the variadic transformation requires the same
* number or less .
*
* When argnames isn ' t NIL , the returned args [ ] type arrays are not ordered
* according to the functions ' declarations , but rather according to the call :
* first any positional arguments , then the named arguments , then defaulted
* arguments ( if needed and allowed by expand_defaults ) . The argnumbers [ ]
* array can be used to map this back to the catalog information .
* argnumbers [ k ] is set to the proargtypes or proallargtypes index of the
* k ' th call argument .
*
* We search a single namespace if the function name is qualified , else
* all namespaces in the search path . In the multiple - namespace case ,
* we arrange for entries in earlier namespaces to mask identical entries in
* later namespaces .
*
* When expanding variadics , we arrange for non - variadic functions to mask
* variadic ones if the expanded argument list is the same . It is still
* possible for there to be conflicts between different variadic functions ,
* however .
*
* It is guaranteed that the return list will never contain multiple entries
* with identical argument lists . When expand_defaults is true , the entries
* could have more than nargs positions , but we still guarantee that they are
* distinct in the first nargs positions . However , if argnames isn ' t NIL or
* either expand_variadic or expand_defaults is true , there might be multiple
* candidate functions that expand to identical argument lists . Rather than
* throw error here , we report such situations by returning a single entry
* with oid = 0 that represents a set of such conflicting candidates .
* The caller might end up discarding such an entry anyway , but if it selects
* such an entry it should react as though the call were ambiguous .
*
* If missing_ok is true , an empty list ( NULL ) is returned if the name was
* schema - qualified with a schema that does not exist . Likewise if no
* candidate is found for other reasons .
*/
FuncCandidateList
FuncnameGetCandidates(List *names, int nargs, List *argnames,
bool expand_variadic, bool expand_defaults,
bool include_out_arguments, bool missing_ok)
{
FuncCandidateList resultList = NULL;
bool any_special = false ;
char *schemaname;
char *funcname;
Oid namespaceId;
CatCList *catlist;
int i;
/* check for caller error */
Assert(nargs >= 0 || !(expand_variadic | expand_defaults));
/* deconstruct the name list */
DeconstructQualifiedName(names, &schemaname, &funcname);
if (schemaname)
{
/* use exact schema given */
namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
if (!OidIsValid(namespaceId))
return NULL;
}
else
{
/* flag to indicate we need namespace search */
namespaceId = InvalidOid;
recomputeNamespacePath();
}
/* Search syscache by name only */
catlist = SearchSysCacheList1(PROCNAMEARGSNSP, CStringGetDatum(funcname));
for (i = 0 ; i < catlist->n_members; i++)
{
HeapTuple proctup = &catlist->members[i]->tuple;
Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
Oid *proargtypes = procform->proargtypes.values;
int pronargs = procform->pronargs;
int effective_nargs;
int pathpos = 0 ;
bool variadic;
bool use_defaults;
Oid va_elem_type;
int *argnumbers = NULL;
FuncCandidateList newResult;
if (OidIsValid(namespaceId))
{
/* Consider only procs in specified namespace */
if (procform->pronamespace != namespaceId)
continue ;
}
else
{
/*
* Consider only procs that are in the search path and are not in
* the temp namespace .
*/
ListCell *nsp;
foreach(nsp, activeSearchPath)
{
if (procform->pronamespace == lfirst_oid(nsp) &&
procform->pronamespace != myTempNamespace)
break ;
pathpos++;
}
if (nsp == NULL)
continue ; /* proc is not in search path */
}
/*
* If we are asked to match to OUT arguments , then use the
* proallargtypes array ( which includes those ) ; otherwise use
* proargtypes ( which doesn ' t ) . Of course , if proallargtypes is null ,
* we always use proargtypes .
*/
if (include_out_arguments)
{
Datum proallargtypes;
bool isNull;
proallargtypes = SysCacheGetAttr(PROCNAMEARGSNSP, proctup,
Anum_pg_proc_proallargtypes,
&isNull);
if (!isNull)
{
ArrayType *arr = DatumGetArrayTypeP(proallargtypes);
pronargs = ARR_DIMS(arr)[0 ];
if (ARR_NDIM(arr) != 1 ||
pronargs < 0 ||
ARR_HASNULL(arr) ||
ARR_ELEMTYPE(arr) != OIDOID)
elog(ERROR, "proallargtypes is not a 1-D Oid array or it contains nulls" );
Assert(pronargs >= procform->pronargs);
proargtypes = (Oid *) ARR_DATA_PTR(arr);
}
}
if (argnames != NIL)
{
/*
* Call uses named or mixed notation
*
* Named or mixed notation can match a variadic function only if
* expand_variadic is off ; otherwise there is no way to match the
* presumed - nameless parameters expanded from the variadic array .
*/
if (OidIsValid(procform->provariadic) && expand_variadic)
continue ;
va_elem_type = InvalidOid;
variadic = false ;
/*
* Check argument count .
*/
Assert(nargs >= 0 ); /* -1 not supported with argnames */
if (pronargs > nargs && expand_defaults)
{
/* Ignore if not enough default expressions */
if (nargs + procform->pronargdefaults < pronargs)
continue ;
use_defaults = true ;
}
else
use_defaults = false ;
/* Ignore if it doesn't match requested argument count */
if (pronargs != nargs && !use_defaults)
continue ;
/* Check for argument name match, generate positional mapping */
if (!MatchNamedCall(proctup, nargs, argnames,
include_out_arguments, pronargs,
&argnumbers))
continue ;
/* Named argument matching is always "special" */
any_special = true ;
}
else
{
/*
* Call uses positional notation
*
* Check if function is variadic , and get variadic element type if
* so . If expand_variadic is false , we should just ignore
* variadic - ness .
*/
if (pronargs <= nargs && expand_variadic)
{
va_elem_type = procform->provariadic;
variadic = OidIsValid(va_elem_type);
any_special |= variadic;
}
else
{
va_elem_type = InvalidOid;
variadic = false ;
}
/*
* Check if function can match by using parameter defaults .
*/
if (pronargs > nargs && expand_defaults)
{
/* Ignore if not enough default expressions */
if (nargs + procform->pronargdefaults < pronargs)
continue ;
use_defaults = true ;
any_special = true ;
}
else
use_defaults = false ;
/* Ignore if it doesn't match requested argument count */
if (nargs >= 0 && pronargs != nargs && !variadic && !use_defaults)
continue ;
}
/*
* We must compute the effective argument list so that we can easily
* compare it to earlier results . We waste a palloc cycle if it gets
* masked by an earlier result , but really that ' s a pretty infrequent
* case so it ' s not worth worrying about .
*/
effective_nargs = Max(pronargs, nargs);
newResult = (FuncCandidateList)
palloc(offsetof(struct _FuncCandidateList, args) +
effective_nargs * sizeof (Oid));
newResult->pathpos = pathpos;
newResult->oid = procform->oid;
newResult->nominalnargs = pronargs;
newResult->nargs = effective_nargs;
newResult->argnumbers = argnumbers;
if (argnumbers)
{
/* Re-order the argument types into call's logical order */
for (int j = 0 ; j < pronargs; j++)
newResult->args[j] = proargtypes[argnumbers[j]];
}
else
{
/* Simple positional case, just copy proargtypes as-is */
memcpy(newResult->args, proargtypes, pronargs * sizeof (Oid));
}
if (variadic)
{
newResult->nvargs = effective_nargs - pronargs + 1 ;
/* Expand variadic argument into N copies of element type */
for (int j = pronargs - 1 ; j < effective_nargs; j++)
newResult->args[j] = va_elem_type;
}
else
newResult->nvargs = 0 ;
newResult->ndargs = use_defaults ? pronargs - nargs : 0 ;
/*
* Does it have the same arguments as something we already accepted ?
* If so , decide what to do to avoid returning duplicate argument
* lists . We can skip this check for the single - namespace case if no
* special ( named , variadic or defaults ) match has been made , since
* then the unique index on pg_proc guarantees all the matches have
* different argument lists .
*/
if (resultList != NULL &&
(any_special || !OidIsValid(namespaceId)))
{
/*
* If we have an ordered list from SearchSysCacheList ( the normal
* case ) , then any conflicting proc must immediately adjoin this
* one in the list , so we only need to look at the newest result
* item . If we have an unordered list , we have to scan the whole
* result list . Also , if either the current candidate or any
* previous candidate is a special match , we can ' t assume that
* conflicts are adjacent .
*
* We ignore defaulted arguments in deciding what is a match .
*/
FuncCandidateList prevResult;
if (catlist->ordered && !any_special)
{
/* ndargs must be 0 if !any_special */
if (effective_nargs == resultList->nargs &&
memcmp(newResult->args,
resultList->args,
effective_nargs * sizeof (Oid)) == 0 )
prevResult = resultList;
else
prevResult = NULL;
}
else
{
int cmp_nargs = newResult->nargs - newResult->ndargs;
for (prevResult = resultList;
prevResult;
prevResult = prevResult->next)
{
if (cmp_nargs == prevResult->nargs - prevResult->ndargs &&
memcmp(newResult->args,
prevResult->args,
cmp_nargs * sizeof (Oid)) == 0 )
break ;
}
}
if (prevResult)
{
/*
* We have a match with a previous result . Decide which one
* to keep , or mark it ambiguous if we can ' t decide . The
* logic here is preference > 0 means prefer the old result ,
* preference < 0 means prefer the new , preference = 0 means
* ambiguous .
*/
int preference;
if (pathpos != prevResult->pathpos)
{
/*
* Prefer the one that ' s earlier in the search path .
*/
preference = pathpos - prevResult->pathpos;
}
else if (variadic && prevResult->nvargs == 0 )
{
/*
* With variadic functions we could have , for example ,
* both foo ( numeric ) and foo ( variadic numeric [ ] ) in the
* same namespace ; if so we prefer the non - variadic match
* on efficiency grounds .
*/
preference = 1 ;
}
else if (!variadic && prevResult->nvargs > 0 )
{
preference = -1 ;
}
else
{
/*----------
* We can ' t decide . This can happen with , for example ,
* both foo ( numeric , variadic numeric [ ] ) and
* foo ( variadic numeric [ ] ) in the same namespace , or
* both foo ( int ) and foo ( int , int default something )
* in the same namespace , or both foo ( a int , b text )
* and foo ( b text , a int ) in the same namespace .
* - - - - - - - - - -
*/
preference = 0 ;
}
if (preference > 0 )
{
/* keep previous result */
pfree(newResult);
continue ;
}
else if (preference < 0 )
{
/* remove previous result from the list */
if (prevResult == resultList)
resultList = prevResult->next;
else
{
FuncCandidateList prevPrevResult;
for (prevPrevResult = resultList;
prevPrevResult;
prevPrevResult = prevPrevResult->next)
{
if (prevResult == prevPrevResult->next)
{
prevPrevResult->next = prevResult->next;
break ;
}
}
Assert(prevPrevResult); /* assert we found it */
}
pfree(prevResult);
/* fall through to add newResult to list */
}
else
{
/* mark old result as ambiguous, discard new */
prevResult->oid = InvalidOid;
pfree(newResult);
continue ;
}
}
}
/*
* Okay to add it to result list
*/
newResult->next = resultList;
resultList = newResult;
}
ReleaseSysCacheList(catlist);
return resultList;
}
/*
* MatchNamedCall
* Given a pg_proc heap tuple and a call ' s list of argument names ,
* check whether the function could match the call .
*
* The call could match if all supplied argument names are accepted by
* the function , in positions after the last positional argument , and there
* are defaults for all unsupplied arguments .
*
* If include_out_arguments is true , we are treating OUT arguments as
* included in the argument list . pronargs is the number of arguments
* we ' re considering ( the length of either proargtypes or proallargtypes ) .
*
* The number of positional arguments is nargs - list_length ( argnames ) .
* Note caller has already done basic checks on argument count .
*
* On match , return true and fill * argnumbers with a palloc ' d array showing
* the mapping from call argument positions to actual function argument
* numbers . Defaulted arguments are included in this map , at positions
* after the last supplied argument .
*/
static bool
MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
bool include_out_arguments, int pronargs,
int **argnumbers)
{
Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
int numposargs = nargs - list_length(argnames);
int pronallargs;
Oid *p_argtypes;
char **p_argnames;
char *p_argmodes;
bool arggiven[FUNC_MAX_ARGS];
bool isnull;
int ap; /* call args position */
int pp; /* proargs position */
ListCell *lc;
Assert(argnames != NIL);
Assert(numposargs >= 0 );
Assert(nargs <= pronargs);
/* Ignore this function if its proargnames is null */
(void ) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proargnames,
&isnull);
if (isnull)
return false ;
/* OK, let's extract the argument names and types */
pronallargs = get_func_arg_info(proctup,
&p_argtypes, &p_argnames, &p_argmodes);
Assert(p_argnames != NULL);
Assert(include_out_arguments ? (pronargs == pronallargs) : (pronargs <= pronallargs));
/* initialize state for matching */
*argnumbers = (int *) palloc(pronargs * sizeof (int ));
memset(arggiven, false , pronargs * sizeof (bool ));
/* there are numposargs positional args before the named args */
for (ap = 0 ; ap < numposargs; ap++)
{
(*argnumbers)[ap] = ap;
arggiven[ap] = true ;
}
/* now examine the named args */
foreach(lc, argnames)
{
char *argname = (char *) lfirst(lc);
bool found;
int i;
pp = 0 ;
found = false ;
for (i = 0 ; i < pronallargs; i++)
{
/* consider only input params, except with include_out_arguments */
if (!include_out_arguments &&
p_argmodes &&
(p_argmodes[i] != FUNC_PARAM_IN &&
p_argmodes[i] != FUNC_PARAM_INOUT &&
p_argmodes[i] != FUNC_PARAM_VARIADIC))
continue ;
if (p_argnames[i] && strcmp(p_argnames[i], argname) == 0 )
{
/* fail if argname matches a positional argument */
if (arggiven[pp])
return false ;
arggiven[pp] = true ;
(*argnumbers)[ap] = pp;
found = true ;
break ;
}
/* increase pp only for considered parameters */
pp++;
}
/* if name isn't in proargnames, fail */
if (!found)
return false ;
ap++;
}
Assert(ap == nargs); /* processed all actual parameters */
/* Check for default arguments */
if (nargs < pronargs)
{
int first_arg_with_default = pronargs - procform->pronargdefaults;
for (pp = numposargs; pp < pronargs; pp++)
{
if (arggiven[pp])
continue ;
/* fail if arg not given and no default available */
if (pp < first_arg_with_default)
return false ;
(*argnumbers)[ap++] = pp;
}
}
Assert(ap == pronargs); /* processed all function parameters */
return true ;
}
/*
* FunctionIsVisible
* Determine whether a function ( identified by OID ) is visible in the
* current search path . Visible means " would be found by searching
* for the unqualified function name with exact argument matches " .
*/
bool
FunctionIsVisible(Oid funcid)
{
return FunctionIsVisibleExt(funcid, NULL);
}
/*
* FunctionIsVisibleExt
* As above , but if the function isn ' t found and is_missing is not NULL ,
* then set * is_missing = true and return false instead of throwing
* an error . ( Caller must initialize * is_missing = false . )
*/
static bool
FunctionIsVisibleExt(Oid funcid, bool *is_missing)
{
HeapTuple proctup;
Form_pg_proc procform;
Oid pronamespace;
bool visible;
proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(proctup))
{
if (is_missing != NULL)
{
*is_missing = true ;
return false ;
}
elog(ERROR, "cache lookup failed for function %u" , funcid);
}
procform = (Form_pg_proc) GETSTRUCT(proctup);
recomputeNamespacePath();
/*
* Quick check : if it ain ' t in the path at all , it ain ' t visible . Items in
* the system namespace are surely in the path and so we needn ' t even do
* list_member_oid ( ) for them .
*/
pronamespace = procform->pronamespace;
if (pronamespace != PG_CATALOG_NAMESPACE &&
!list_member_oid(activeSearchPath, pronamespace))
visible = false ;
else
{
/*
* If it is in the path , it might still not be visible ; it could be
* hidden by another proc of the same name and arguments earlier in
* the path . So we must do a slow check to see if this is the same
* proc that would be found by FuncnameGetCandidates .
*/
char *proname = NameStr(procform->proname);
int nargs = procform->pronargs;
FuncCandidateList clist;
visible = false ;
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
nargs, NIL, false , false , false , false );
for (; clist; clist = clist->next)
{
if (memcmp(clist->args, procform->proargtypes.values,
nargs * sizeof (Oid)) == 0 )
{
/* Found the expected entry; is it the right proc? */
visible = (clist->oid == funcid);
break ;
}
}
}
ReleaseSysCache(proctup);
return visible;
}
/*
* OpernameGetOprid
* Given a possibly - qualified operator name and exact input datatypes ,
* look up the operator . Returns InvalidOid if not found .
*
* Pass oprleft = InvalidOid for a prefix op .
*
* If the operator name is not schema - qualified , it is sought in the current
* namespace search path . If the name is schema - qualified and the given
* schema does not exist , InvalidOid is returned .
*/
Oid
OpernameGetOprid(List *names, Oid oprleft, Oid oprright)
{
char *schemaname;
char *opername;
CatCList *catlist;
ListCell *l;
/* deconstruct the name list */
DeconstructQualifiedName(names, &schemaname, &opername);
if (schemaname)
{
/* search only in exact schema given */
Oid namespaceId;
namespaceId = LookupExplicitNamespace(schemaname, true );
if (OidIsValid(namespaceId))
{
HeapTuple opertup;
opertup = SearchSysCache4(OPERNAMENSP,
CStringGetDatum(opername),
ObjectIdGetDatum(oprleft),
ObjectIdGetDatum(oprright),
ObjectIdGetDatum(namespaceId));
if (HeapTupleIsValid(opertup))
{
Form_pg_operator operclass = (Form_pg_operator) GETSTRUCT(opertup);
Oid result = operclass->oid;
ReleaseSysCache(opertup);
return result;
}
}
return InvalidOid;
}
/* Search syscache by name and argument types */
catlist = SearchSysCacheList3(OPERNAMENSP,
CStringGetDatum(opername),
ObjectIdGetDatum(oprleft),
ObjectIdGetDatum(oprright));
if (catlist->n_members == 0 )
{
/* no hope, fall out early */
ReleaseSysCacheList(catlist);
return InvalidOid;
}
/*
* We have to find the list member that is first in the search path , if
* there ' s more than one . This doubly - nested loop looks ugly , but in
* practice there should usually be few catlist members .
*/
recomputeNamespacePath();
foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
int i;
if (namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
for (i = 0 ; i < catlist->n_members; i++)
{
HeapTuple opertup = &catlist->members[i]->tuple;
Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
if (operform->oprnamespace == namespaceId)
{
Oid result = operform->oid;
ReleaseSysCacheList(catlist);
return result;
}
}
}
ReleaseSysCacheList(catlist);
return InvalidOid;
}
/*
* OpernameGetCandidates
* Given a possibly - qualified operator name and operator kind ,
* retrieve a list of the possible matches .
*
* If oprkind is ' \ 0 ' , we return all operators matching the given name ,
* regardless of arguments .
*
* We search a single namespace if the operator name is qualified , else
* all namespaces in the search path . The return list will never contain
* multiple entries with identical argument lists - - - in the multiple -
* namespace case , we arrange for entries in earlier namespaces to mask
* identical entries in later namespaces .
*
* The returned items always have two args [ ] entries - - - the first will be
* InvalidOid for a prefix oprkind . nargs is always 2 , too .
*/
FuncCandidateList
OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok)
{
FuncCandidateList resultList = NULL;
char *resultSpace = NULL;
int nextResult = 0 ;
char *schemaname;
char *opername;
Oid namespaceId;
CatCList *catlist;
int i;
/* deconstruct the name list */
DeconstructQualifiedName(names, &schemaname, &opername);
if (schemaname)
{
/* use exact schema given */
namespaceId = LookupExplicitNamespace(schemaname, missing_schema_ok);
if (missing_schema_ok && !OidIsValid(namespaceId))
return NULL;
}
else
{
/* flag to indicate we need namespace search */
namespaceId = InvalidOid;
recomputeNamespacePath();
}
/* Search syscache by name only */
catlist = SearchSysCacheList1(OPERNAMENSP, CStringGetDatum(opername));
/*
* In typical scenarios , most if not all of the operators found by the
* catcache search will end up getting returned ; and there can be quite a
* few , for common operator names such as ' = ' or ' + ' . To reduce the time
* spent in palloc , we allocate the result space as an array large enough
* to hold all the operators . The original coding of this routine did a
* separate palloc for each operator , but profiling revealed that the
* pallocs used an unreasonably large fraction of parsing time .
*/
#define SPACE_PER_OP MAXALIGN(offsetof(struct _FuncCandidateList, args) + \
2 * sizeof (Oid))
if (catlist->n_members > 0 )
resultSpace = palloc(catlist->n_members * SPACE_PER_OP);
for (i = 0 ; i < catlist->n_members; i++)
{
HeapTuple opertup = &catlist->members[i]->tuple;
Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
int pathpos = 0 ;
FuncCandidateList newResult;
/* Ignore operators of wrong kind, if specific kind requested */
if (oprkind && operform->oprkind != oprkind)
continue ;
if (OidIsValid(namespaceId))
{
/* Consider only opers in specified namespace */
if (operform->oprnamespace != namespaceId)
continue ;
/* No need to check args, they must all be different */
}
else
{
/*
* Consider only opers that are in the search path and are not in
* the temp namespace .
*/
ListCell *nsp;
foreach(nsp, activeSearchPath)
{
if (operform->oprnamespace == lfirst_oid(nsp) &&
operform->oprnamespace != myTempNamespace)
break ;
pathpos++;
}
if (nsp == NULL)
continue ; /* oper is not in search path */
/*
* Okay , it ' s in the search path , but does it have the same
* arguments as something we already accepted ? If so , keep only
* the one that appears earlier in the search path .
*
* If we have an ordered list from SearchSysCacheList ( the normal
* case ) , then any conflicting oper must immediately adjoin this
* one in the list , so we only need to look at the newest result
* item . If we have an unordered list , we have to scan the whole
* result list .
*/
if (resultList)
{
FuncCandidateList prevResult;
if (catlist->ordered)
{
if (operform->oprleft == resultList->args[0 ] &&
operform->oprright == resultList->args[1 ])
prevResult = resultList;
else
prevResult = NULL;
}
else
{
for (prevResult = resultList;
prevResult;
prevResult = prevResult->next)
{
if (operform->oprleft == prevResult->args[0 ] &&
operform->oprright == prevResult->args[1 ])
break ;
}
}
if (prevResult)
{
/* We have a match with a previous result */
Assert(pathpos != prevResult->pathpos);
if (pathpos > prevResult->pathpos)
continue ; /* keep previous result */
/* replace previous result */
prevResult->pathpos = pathpos;
prevResult->oid = operform->oid;
continue ; /* args are same, of course */
}
}
}
/*
* Okay to add it to result list
*/
newResult = (FuncCandidateList) (resultSpace + nextResult);
nextResult += SPACE_PER_OP;
newResult->pathpos = pathpos;
newResult->oid = operform->oid;
newResult->nominalnargs = 2 ;
newResult->nargs = 2 ;
newResult->nvargs = 0 ;
newResult->ndargs = 0 ;
newResult->argnumbers = NULL;
newResult->args[0 ] = operform->oprleft;
newResult->args[1 ] = operform->oprright;
newResult->next = resultList;
resultList = newResult;
}
ReleaseSysCacheList(catlist);
return resultList;
}
/*
* OperatorIsVisible
* Determine whether an operator ( identified by OID ) is visible in the
* current search path . Visible means " would be found by searching
* for the unqualified operator name with exact argument matches " .
*/
bool
OperatorIsVisible(Oid oprid)
{
return OperatorIsVisibleExt(oprid, NULL);
}
/*
* OperatorIsVisibleExt
* As above , but if the operator isn ' t found and is_missing is not NULL ,
* then set * is_missing = true and return false instead of throwing
* an error . ( Caller must initialize * is_missing = false . )
*/
static bool
OperatorIsVisibleExt(Oid oprid, bool *is_missing)
{
HeapTuple oprtup;
Form_pg_operator oprform;
Oid oprnamespace;
bool visible;
oprtup = SearchSysCache1(OPEROID, ObjectIdGetDatum(oprid));
if (!HeapTupleIsValid(oprtup))
{
if (is_missing != NULL)
{
*is_missing = true ;
return false ;
}
elog(ERROR, "cache lookup failed for operator %u" , oprid);
}
oprform = (Form_pg_operator) GETSTRUCT(oprtup);
recomputeNamespacePath();
/*
* Quick check : if it ain ' t in the path at all , it ain ' t visible . Items in
* the system namespace are surely in the path and so we needn ' t even do
* list_member_oid ( ) for them .
*/
oprnamespace = oprform->oprnamespace;
if (oprnamespace != PG_CATALOG_NAMESPACE &&
!list_member_oid(activeSearchPath, oprnamespace))
visible = false ;
else
{
/*
* If it is in the path , it might still not be visible ; it could be
* hidden by another operator of the same name and arguments earlier
* in the path . So we must do a slow check to see if this is the same
* operator that would be found by OpernameGetOprid .
*/
char *oprname = NameStr(oprform->oprname);
visible = (OpernameGetOprid(list_make1(makeString(oprname)),
oprform->oprleft, oprform->oprright)
== oprid);
}
ReleaseSysCache(oprtup);
return visible;
}
/*
* OpclassnameGetOpcid
* Try to resolve an unqualified index opclass name .
* Returns OID if opclass found in search path , else InvalidOid .
*
* This is essentially the same as TypenameGetTypid , but we have to have
* an extra argument for the index AM OID .
*/
Oid
OpclassnameGetOpcid(Oid amid, const char *opcname)
{
Oid opcid;
ListCell *l;
recomputeNamespacePath();
foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
opcid = GetSysCacheOid3(CLAAMNAMENSP, Anum_pg_opclass_oid,
ObjectIdGetDatum(amid),
PointerGetDatum(opcname),
ObjectIdGetDatum(namespaceId));
if (OidIsValid(opcid))
return opcid;
}
/* Not found in path */
return InvalidOid;
}
/*
* OpclassIsVisible
* Determine whether an opclass ( identified by OID ) is visible in the
* current search path . Visible means " would be found by searching
* for the unqualified opclass name " .
*/
bool
OpclassIsVisible(Oid opcid)
{
return OpclassIsVisibleExt(opcid, NULL);
}
/*
* OpclassIsVisibleExt
* As above , but if the opclass isn ' t found and is_missing is not NULL ,
* then set * is_missing = true and return false instead of throwing
* an error . ( Caller must initialize * is_missing = false . )
*/
static bool
OpclassIsVisibleExt(Oid opcid, bool *is_missing)
{
HeapTuple opctup;
Form_pg_opclass opcform;
Oid opcnamespace;
bool visible;
opctup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcid));
if (!HeapTupleIsValid(opctup))
{
if (is_missing != NULL)
{
*is_missing = true ;
return false ;
}
elog(ERROR, "cache lookup failed for opclass %u" , opcid);
}
opcform = (Form_pg_opclass) GETSTRUCT(opctup);
recomputeNamespacePath();
/*
* Quick check : if it ain ' t in the path at all , it ain ' t visible . Items in
* the system namespace are surely in the path and so we needn ' t even do
* list_member_oid ( ) for them .
*/
opcnamespace = opcform->opcnamespace;
if (opcnamespace != PG_CATALOG_NAMESPACE &&
!list_member_oid(activeSearchPath, opcnamespace))
visible = false ;
else
{
/*
* If it is in the path , it might still not be visible ; it could be
* hidden by another opclass of the same name earlier in the path . So
* we must do a slow check to see if this opclass would be found by
* OpclassnameGetOpcid .
*/
char *opcname = NameStr(opcform->opcname);
visible = (OpclassnameGetOpcid(opcform->opcmethod, opcname) == opcid);
}
ReleaseSysCache(opctup);
return visible;
}
/*
* OpfamilynameGetOpfid
* Try to resolve an unqualified index opfamily name .
* Returns OID if opfamily found in search path , else InvalidOid .
*
* This is essentially the same as TypenameGetTypid , but we have to have
* an extra argument for the index AM OID .
*/
Oid
OpfamilynameGetOpfid(Oid amid, const char *opfname)
{
Oid opfid;
ListCell *l;
recomputeNamespacePath();
foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
opfid = GetSysCacheOid3(OPFAMILYAMNAMENSP, Anum_pg_opfamily_oid,
ObjectIdGetDatum(amid),
PointerGetDatum(opfname),
ObjectIdGetDatum(namespaceId));
if (OidIsValid(opfid))
return opfid;
}
/* Not found in path */
return InvalidOid;
}
/*
* OpfamilyIsVisible
* Determine whether an opfamily ( identified by OID ) is visible in the
* current search path . Visible means " would be found by searching
* for the unqualified opfamily name " .
*/
bool
OpfamilyIsVisible(Oid opfid)
{
return OpfamilyIsVisibleExt(opfid, NULL);
}
/*
* OpfamilyIsVisibleExt
* As above , but if the opfamily isn ' t found and is_missing is not NULL ,
* then set * is_missing = true and return false instead of throwing
* an error . ( Caller must initialize * is_missing = false . )
*/
static bool
OpfamilyIsVisibleExt(Oid opfid, bool *is_missing)
{
HeapTuple opftup;
Form_pg_opfamily opfform;
Oid opfnamespace;
bool visible;
opftup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfid));
if (!HeapTupleIsValid(opftup))
{
if (is_missing != NULL)
{
*is_missing = true ;
return false ;
}
elog(ERROR, "cache lookup failed for opfamily %u" , opfid);
}
opfform = (Form_pg_opfamily) GETSTRUCT(opftup);
recomputeNamespacePath();
/*
* Quick check : if it ain ' t in the path at all , it ain ' t visible . Items in
* the system namespace are surely in the path and so we needn ' t even do
* list_member_oid ( ) for them .
*/
opfnamespace = opfform->opfnamespace;
if (opfnamespace != PG_CATALOG_NAMESPACE &&
!list_member_oid(activeSearchPath, opfnamespace))
visible = false ;
else
{
/*
* If it is in the path , it might still not be visible ; it could be
* hidden by another opfamily of the same name earlier in the path . So
* we must do a slow check to see if this opfamily would be found by
* OpfamilynameGetOpfid .
*/
char *opfname = NameStr(opfform->opfname);
visible = (OpfamilynameGetOpfid(opfform->opfmethod, opfname) == opfid);
}
ReleaseSysCache(opftup);
return visible;
}
/*
* lookup_collation
* If there ' s a collation of the given name / namespace , and it works
* with the given encoding , return its OID . Else return InvalidOid .
*/
static Oid
lookup_collation(const char *collname, Oid collnamespace, int32 encoding)
{
Oid collid;
HeapTuple colltup;
Form_pg_collation collform;
/* Check for encoding-specific entry (exact match) */
collid = GetSysCacheOid3(COLLNAMEENCNSP, Anum_pg_collation_oid,
PointerGetDatum(collname),
Int32GetDatum(encoding),
ObjectIdGetDatum(collnamespace));
if (OidIsValid(collid))
return collid;
/*
* Check for any - encoding entry . This takes a bit more work : while libc
* collations with collencoding = - 1 do work with all encodings , ICU
* collations only work with certain encodings , so we have to check that
* aspect before deciding it ' s a match .
*/
colltup = SearchSysCache3(COLLNAMEENCNSP,
PointerGetDatum(collname),
Int32GetDatum(-1 ),
ObjectIdGetDatum(collnamespace));
if (!HeapTupleIsValid(colltup))
return InvalidOid;
collform = (Form_pg_collation) GETSTRUCT(colltup);
if (collform->collprovider == COLLPROVIDER_ICU)
{
if (is_encoding_supported_by_icu(encoding))
collid = collform->oid;
else
collid = InvalidOid;
}
else
{
collid = collform->oid;
}
ReleaseSysCache(colltup);
return collid;
}
/*
* CollationGetCollid
* Try to resolve an unqualified collation name .
* Returns OID if collation found in search path , else InvalidOid .
*
* Note that this will only find collations that work with the current
* database ' s encoding .
*/
Oid
CollationGetCollid(const char *collname)
{
int32 dbencoding = GetDatabaseEncoding();
ListCell *l;
recomputeNamespacePath();
foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
Oid collid;
if (namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
collid = lookup_collation(collname, namespaceId, dbencoding);
if (OidIsValid(collid))
return collid;
}
/* Not found in path */
return InvalidOid;
}
/*
* CollationIsVisible
* Determine whether a collation ( identified by OID ) is visible in the
* current search path . Visible means " would be found by searching
* for the unqualified collation name " .
*
* Note that only collations that work with the current database ' s encoding
* will be considered visible .
*/
bool
CollationIsVisible(Oid collid)
{
return CollationIsVisibleExt(collid, NULL);
}
/*
* CollationIsVisibleExt
* As above , but if the collation isn ' t found and is_missing is not NULL ,
* then set * is_missing = true and return false instead of throwing
* an error . ( Caller must initialize * is_missing = false . )
*/
static bool
CollationIsVisibleExt(Oid collid, bool *is_missing)
{
HeapTuple colltup;
Form_pg_collation collform;
Oid collnamespace;
bool visible;
colltup = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
if (!HeapTupleIsValid(colltup))
{
if (is_missing != NULL)
{
*is_missing = true ;
return false ;
}
elog(ERROR, "cache lookup failed for collation %u" , collid);
}
collform = (Form_pg_collation) GETSTRUCT(colltup);
recomputeNamespacePath();
/*
* Quick check : if it ain ' t in the path at all , it ain ' t visible . Items in
* the system namespace are surely in the path and so we needn ' t even do
* list_member_oid ( ) for them .
*/
collnamespace = collform->collnamespace;
if (collnamespace != PG_CATALOG_NAMESPACE &&
!list_member_oid(activeSearchPath, collnamespace))
visible = false ;
else
{
/*
* If it is in the path , it might still not be visible ; it could be
* hidden by another collation of the same name earlier in the path ,
* or it might not work with the current DB encoding . So we must do a
* slow check to see if this collation would be found by
* CollationGetCollid .
*/
char *collname = NameStr(collform->collname);
visible = (CollationGetCollid(collname) == collid);
}
ReleaseSysCache(colltup);
return visible;
}
/*
* ConversionGetConid
* Try to resolve an unqualified conversion name .
* Returns OID if conversion found in search path , else InvalidOid .
*
* This is essentially the same as RelnameGetRelid .
*/
Oid
ConversionGetConid(const char *conname)
{
Oid conid;
ListCell *l;
recomputeNamespacePath();
foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
conid = GetSysCacheOid2(CONNAMENSP, Anum_pg_conversion_oid,
PointerGetDatum(conname),
ObjectIdGetDatum(namespaceId));
if (OidIsValid(conid))
return conid;
}
/* Not found in path */
return InvalidOid;
}
/*
* ConversionIsVisible
* Determine whether a conversion ( identified by OID ) is visible in the
* current search path . Visible means " would be found by searching
* for the unqualified conversion name " .
*/
bool
ConversionIsVisible(Oid conid)
{
return ConversionIsVisibleExt(conid, NULL);
}
/*
* ConversionIsVisibleExt
* As above , but if the conversion isn ' t found and is_missing is not NULL ,
* then set * is_missing = true and return false instead of throwing
* an error . ( Caller must initialize * is_missing = false . )
*/
static bool
ConversionIsVisibleExt(Oid conid, bool *is_missing)
{
HeapTuple contup;
Form_pg_conversion conform;
Oid connamespace;
bool visible;
contup = SearchSysCache1(CONVOID, ObjectIdGetDatum(conid));
if (!HeapTupleIsValid(contup))
{
if (is_missing != NULL)
{
*is_missing = true ;
return false ;
}
elog(ERROR, "cache lookup failed for conversion %u" , conid);
}
conform = (Form_pg_conversion) GETSTRUCT(contup);
recomputeNamespacePath();
/*
* Quick check : if it ain ' t in the path at all , it ain ' t visible . Items in
* the system namespace are surely in the path and so we needn ' t even do
* list_member_oid ( ) for them .
*/
connamespace = conform->connamespace;
if (connamespace != PG_CATALOG_NAMESPACE &&
!list_member_oid(activeSearchPath, connamespace))
visible = false ;
else
{
/*
* If it is in the path , it might still not be visible ; it could be
* hidden by another conversion of the same name earlier in the path .
* So we must do a slow check to see if this conversion would be found
* by ConversionGetConid .
*/
char *conname = NameStr(conform->conname);
visible = (ConversionGetConid(conname) == conid);
}
ReleaseSysCache(contup);
return visible;
}
/*
* get_statistics_object_oid - find a statistics object by possibly qualified name
*
* If not found , returns InvalidOid if missing_ok , else throws error
*/
Oid
get_statistics_object_oid(List *names, bool missing_ok)
{
char *schemaname;
char *stats_name;
Oid namespaceId;
Oid stats_oid = InvalidOid;
ListCell *l;
/* deconstruct the name list */
DeconstructQualifiedName(names, &schemaname, &stats_name);
if (schemaname)
{
/* use exact schema given */
namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
if (missing_ok && !OidIsValid(namespaceId))
stats_oid = InvalidOid;
else
stats_oid = GetSysCacheOid2(STATEXTNAMENSP, Anum_pg_statistic_ext_oid,
PointerGetDatum(stats_name),
ObjectIdGetDatum(namespaceId));
}
else
{
/* search for it in search path */
recomputeNamespacePath();
foreach(l, activeSearchPath)
{
namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
stats_oid = GetSysCacheOid2(STATEXTNAMENSP, Anum_pg_statistic_ext_oid,
PointerGetDatum(stats_name),
ObjectIdGetDatum(namespaceId));
if (OidIsValid(stats_oid))
break ;
}
}
if (!OidIsValid(stats_oid) && !missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("statistics object \" %s\" does not exist" ,
NameListToString(names))));
return stats_oid;
}
/*
* StatisticsObjIsVisible
* Determine whether a statistics object ( identified by OID ) is visible in
* the current search path . Visible means " would be found by searching
* for the unqualified statistics object name " .
*/
bool
StatisticsObjIsVisible(Oid stxid)
{
return StatisticsObjIsVisibleExt(stxid, NULL);
}
/*
* StatisticsObjIsVisibleExt
* As above , but if the statistics object isn ' t found and is_missing is
* not NULL , then set * is_missing = true and return false instead of
* throwing an error . ( Caller must initialize * is_missing = false . )
*/
static bool
StatisticsObjIsVisibleExt(Oid stxid, bool *is_missing)
{
HeapTuple stxtup;
Form_pg_statistic_ext stxform;
Oid stxnamespace;
bool visible;
stxtup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(stxid));
if (!HeapTupleIsValid(stxtup))
{
if (is_missing != NULL)
{
*is_missing = true ;
return false ;
}
elog(ERROR, "cache lookup failed for statistics object %u" , stxid);
}
stxform = (Form_pg_statistic_ext) GETSTRUCT(stxtup);
recomputeNamespacePath();
/*
* Quick check : if it ain ' t in the path at all , it ain ' t visible . Items in
* the system namespace are surely in the path and so we needn ' t even do
* list_member_oid ( ) for them .
*/
stxnamespace = stxform->stxnamespace;
if (stxnamespace != PG_CATALOG_NAMESPACE &&
!list_member_oid(activeSearchPath, stxnamespace))
visible = false ;
else
{
/*
* If it is in the path , it might still not be visible ; it could be
* hidden by another statistics object of the same name earlier in the
* path . So we must do a slow check for conflicting objects .
*/
char *stxname = NameStr(stxform->stxname);
ListCell *l;
visible = false ;
foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
if (namespaceId == stxnamespace)
{
/* Found it first in path */
visible = true ;
break ;
}
if (SearchSysCacheExists2(STATEXTNAMENSP,
PointerGetDatum(stxname),
ObjectIdGetDatum(namespaceId)))
{
/* Found something else first in path */
break ;
}
}
}
ReleaseSysCache(stxtup);
return visible;
}
/*
* get_ts_parser_oid - find a TS parser by possibly qualified name
*
* If not found , returns InvalidOid if missing_ok , else throws error
*/
Oid
get_ts_parser_oid(List *names, bool missing_ok)
{
char *schemaname;
char *parser_name;
Oid namespaceId;
Oid prsoid = InvalidOid;
ListCell *l;
/* deconstruct the name list */
DeconstructQualifiedName(names, &schemaname, &parser_name);
if (schemaname)
{
/* use exact schema given */
namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
if (missing_ok && !OidIsValid(namespaceId))
prsoid = InvalidOid;
else
prsoid = GetSysCacheOid2(TSPARSERNAMENSP, Anum_pg_ts_parser_oid,
PointerGetDatum(parser_name),
ObjectIdGetDatum(namespaceId));
}
else
{
/* search for it in search path */
recomputeNamespacePath();
foreach(l, activeSearchPath)
{
namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
prsoid = GetSysCacheOid2(TSPARSERNAMENSP, Anum_pg_ts_parser_oid,
PointerGetDatum(parser_name),
ObjectIdGetDatum(namespaceId));
if (OidIsValid(prsoid))
break ;
}
}
if (!OidIsValid(prsoid) && !missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("text search parser \" %s\" does not exist" ,
NameListToString(names))));
return prsoid;
}
/*
* TSParserIsVisible
* Determine whether a parser ( identified by OID ) is visible in the
* current search path . Visible means " would be found by searching
* for the unqualified parser name " .
*/
bool
TSParserIsVisible(Oid prsId)
{
return TSParserIsVisibleExt(prsId, NULL);
}
/*
* TSParserIsVisibleExt
* As above , but if the parser isn ' t found and is_missing is not NULL ,
* then set * is_missing = true and return false instead of throwing
* an error . ( Caller must initialize * is_missing = false . )
*/
static bool
TSParserIsVisibleExt(Oid prsId, bool *is_missing)
{
HeapTuple tup;
Form_pg_ts_parser form;
Oid namespace ;
bool visible;
tup = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(prsId));
if (!HeapTupleIsValid(tup))
{
if (is_missing != NULL)
{
*is_missing = true ;
return false ;
}
elog(ERROR, "cache lookup failed for text search parser %u" , prsId);
}
form = (Form_pg_ts_parser) GETSTRUCT(tup);
recomputeNamespacePath();
/*
* Quick check : if it ain ' t in the path at all , it ain ' t visible . Items in
* the system namespace are surely in the path and so we needn ' t even do
* list_member_oid ( ) for them .
*/
namespace = form->prsnamespace;
if (namespace != PG_CATALOG_NAMESPACE &&
!list_member_oid(activeSearchPath, namespace ))
visible = false ;
else
{
/*
* If it is in the path , it might still not be visible ; it could be
* hidden by another parser of the same name earlier in the path . So
* we must do a slow check for conflicting parsers .
*/
char *name = NameStr(form->prsname);
ListCell *l;
visible = false ;
foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
if (namespaceId == namespace )
{
/* Found it first in path */
visible = true ;
break ;
}
if (SearchSysCacheExists2(TSPARSERNAMENSP,
PointerGetDatum(name),
ObjectIdGetDatum(namespaceId)))
{
/* Found something else first in path */
break ;
}
}
}
ReleaseSysCache(tup);
return visible;
}
/*
* get_ts_dict_oid - find a TS dictionary by possibly qualified name
*
* If not found , returns InvalidOid if missing_ok , else throws error
*/
Oid
get_ts_dict_oid(List *names, bool missing_ok)
{
char *schemaname;
char *dict_name;
Oid namespaceId;
Oid dictoid = InvalidOid;
ListCell *l;
/* deconstruct the name list */
DeconstructQualifiedName(names, &schemaname, &dict_name);
if (schemaname)
{
/* use exact schema given */
namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
if (missing_ok && !OidIsValid(namespaceId))
dictoid = InvalidOid;
else
dictoid = GetSysCacheOid2(TSDICTNAMENSP, Anum_pg_ts_dict_oid,
PointerGetDatum(dict_name),
ObjectIdGetDatum(namespaceId));
}
else
{
/* search for it in search path */
recomputeNamespacePath();
foreach(l, activeSearchPath)
{
namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
dictoid = GetSysCacheOid2(TSDICTNAMENSP, Anum_pg_ts_dict_oid,
PointerGetDatum(dict_name),
ObjectIdGetDatum(namespaceId));
if (OidIsValid(dictoid))
break ;
}
}
if (!OidIsValid(dictoid) && !missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("text search dictionary \" %s\" does not exist" ,
NameListToString(names))));
return dictoid;
}
/*
* TSDictionaryIsVisible
* Determine whether a dictionary ( identified by OID ) is visible in the
* current search path . Visible means " would be found by searching
* for the unqualified dictionary name " .
*/
bool
TSDictionaryIsVisible(Oid dictId)
{
return TSDictionaryIsVisibleExt(dictId, NULL);
}
/*
* TSDictionaryIsVisibleExt
* As above , but if the dictionary isn ' t found and is_missing is not NULL ,
* then set * is_missing = true and return false instead of throwing
* an error . ( Caller must initialize * is_missing = false . )
*/
static bool
TSDictionaryIsVisibleExt(Oid dictId, bool *is_missing)
{
HeapTuple tup;
Form_pg_ts_dict form;
Oid namespace ;
bool visible;
tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
if (!HeapTupleIsValid(tup))
{
if (is_missing != NULL)
{
*is_missing = true ;
return false ;
}
elog(ERROR, "cache lookup failed for text search dictionary %u" ,
dictId);
}
form = (Form_pg_ts_dict) GETSTRUCT(tup);
recomputeNamespacePath();
/*
* Quick check : if it ain ' t in the path at all , it ain ' t visible . Items in
* the system namespace are surely in the path and so we needn ' t even do
* list_member_oid ( ) for them .
*/
namespace = form->dictnamespace;
if (namespace != PG_CATALOG_NAMESPACE &&
!list_member_oid(activeSearchPath, namespace ))
visible = false ;
else
{
/*
* If it is in the path , it might still not be visible ; it could be
* hidden by another dictionary of the same name earlier in the path .
* So we must do a slow check for conflicting dictionaries .
*/
char *name = NameStr(form->dictname);
ListCell *l;
visible = false ;
foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
if (namespaceId == namespace )
{
/* Found it first in path */
visible = true ;
break ;
}
if (SearchSysCacheExists2(TSDICTNAMENSP,
PointerGetDatum(name),
ObjectIdGetDatum(namespaceId)))
{
/* Found something else first in path */
break ;
}
}
}
ReleaseSysCache(tup);
return visible;
}
/*
* get_ts_template_oid - find a TS template by possibly qualified name
*
* If not found , returns InvalidOid if missing_ok , else throws error
*/
Oid
get_ts_template_oid(List *names, bool missing_ok)
{
char *schemaname;
char *template_name;
Oid namespaceId;
Oid tmploid = InvalidOid;
ListCell *l;
/* deconstruct the name list */
DeconstructQualifiedName(names, &schemaname, &template_name);
if (schemaname)
{
/* use exact schema given */
namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
if (missing_ok && !OidIsValid(namespaceId))
tmploid = InvalidOid;
else
tmploid = GetSysCacheOid2(TSTEMPLATENAMENSP, Anum_pg_ts_template_oid,
PointerGetDatum(template_name),
ObjectIdGetDatum(namespaceId));
}
else
{
/* search for it in search path */
recomputeNamespacePath();
foreach(l, activeSearchPath)
{
namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
tmploid = GetSysCacheOid2(TSTEMPLATENAMENSP, Anum_pg_ts_template_oid,
PointerGetDatum(template_name),
ObjectIdGetDatum(namespaceId));
if (OidIsValid(tmploid))
break ;
}
}
if (!OidIsValid(tmploid) && !missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("text search template \" %s\" does not exist" ,
NameListToString(names))));
return tmploid;
}
/*
* TSTemplateIsVisible
* Determine whether a template ( identified by OID ) is visible in the
* current search path . Visible means " would be found by searching
* for the unqualified template name " .
*/
bool
TSTemplateIsVisible(Oid tmplId)
{
return TSTemplateIsVisibleExt(tmplId, NULL);
}
/*
* TSTemplateIsVisibleExt
* As above , but if the template isn ' t found and is_missing is not NULL ,
* then set * is_missing = true and return false instead of throwing
* an error . ( Caller must initialize * is_missing = false . )
*/
static bool
TSTemplateIsVisibleExt(Oid tmplId, bool *is_missing)
{
HeapTuple tup;
Form_pg_ts_template form;
Oid namespace ;
bool visible;
tup = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId));
if (!HeapTupleIsValid(tup))
{
if (is_missing != NULL)
{
*is_missing = true ;
return false ;
}
elog(ERROR, "cache lookup failed for text search template %u" , tmplId);
}
form = (Form_pg_ts_template) GETSTRUCT(tup);
recomputeNamespacePath();
/*
* Quick check : if it ain ' t in the path at all , it ain ' t visible . Items in
* the system namespace are surely in the path and so we needn ' t even do
* list_member_oid ( ) for them .
*/
namespace = form->tmplnamespace;
if (namespace != PG_CATALOG_NAMESPACE &&
!list_member_oid(activeSearchPath, namespace ))
visible = false ;
else
{
/*
* If it is in the path , it might still not be visible ; it could be
* hidden by another template of the same name earlier in the path . So
* we must do a slow check for conflicting templates .
*/
char *name = NameStr(form->tmplname);
ListCell *l;
visible = false ;
foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
if (namespaceId == namespace )
{
/* Found it first in path */
visible = true ;
break ;
}
if (SearchSysCacheExists2(TSTEMPLATENAMENSP,
PointerGetDatum(name),
ObjectIdGetDatum(namespaceId)))
{
/* Found something else first in path */
break ;
}
}
}
ReleaseSysCache(tup);
return visible;
}
/*
* get_ts_config_oid - find a TS config by possibly qualified name
*
* If not found , returns InvalidOid if missing_ok , else throws error
*/
Oid
get_ts_config_oid(List *names, bool missing_ok)
{
char *schemaname;
char *config_name;
Oid namespaceId;
Oid cfgoid = InvalidOid;
ListCell *l;
/* deconstruct the name list */
DeconstructQualifiedName(names, &schemaname, &config_name);
if (schemaname)
{
/* use exact schema given */
namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
if (missing_ok && !OidIsValid(namespaceId))
cfgoid = InvalidOid;
else
cfgoid = GetSysCacheOid2(TSCONFIGNAMENSP, Anum_pg_ts_config_oid,
PointerGetDatum(config_name),
ObjectIdGetDatum(namespaceId));
}
else
{
/* search for it in search path */
recomputeNamespacePath();
foreach(l, activeSearchPath)
{
namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
cfgoid = GetSysCacheOid2(TSCONFIGNAMENSP, Anum_pg_ts_config_oid,
PointerGetDatum(config_name),
ObjectIdGetDatum(namespaceId));
if (OidIsValid(cfgoid))
break ;
}
}
if (!OidIsValid(cfgoid) && !missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("text search configuration \" %s\" does not exist" ,
NameListToString(names))));
return cfgoid;
}
/*
* TSConfigIsVisible
* Determine whether a text search configuration ( identified by OID )
* is visible in the current search path . Visible means " would be found
* by searching for the unqualified text search configuration name " .
*/
bool
TSConfigIsVisible(Oid cfgid)
{
return TSConfigIsVisibleExt(cfgid, NULL);
}
/*
* TSConfigIsVisibleExt
* As above , but if the configuration isn ' t found and is_missing is not
* NULL , then set * is_missing = true and return false instead of throwing
* an error . ( Caller must initialize * is_missing = false . )
*/
static bool
TSConfigIsVisibleExt(Oid cfgid, bool *is_missing)
{
HeapTuple tup;
Form_pg_ts_config form;
Oid namespace ;
bool visible;
tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgid));
if (!HeapTupleIsValid(tup))
{
if (is_missing != NULL)
{
*is_missing = true ;
return false ;
}
elog(ERROR, "cache lookup failed for text search configuration %u" ,
cfgid);
}
form = (Form_pg_ts_config) GETSTRUCT(tup);
recomputeNamespacePath();
/*
* Quick check : if it ain ' t in the path at all , it ain ' t visible . Items in
* the system namespace are surely in the path and so we needn ' t even do
* list_member_oid ( ) for them .
*/
namespace = form->cfgnamespace;
if (namespace != PG_CATALOG_NAMESPACE &&
!list_member_oid(activeSearchPath, namespace ))
visible = false ;
else
{
/*
* If it is in the path , it might still not be visible ; it could be
* hidden by another configuration of the same name earlier in the
* path . So we must do a slow check for conflicting configurations .
*/
char *name = NameStr(form->cfgname);
ListCell *l;
visible = false ;
foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
if (namespaceId == namespace )
{
/* Found it first in path */
visible = true ;
break ;
}
if (SearchSysCacheExists2(TSCONFIGNAMENSP,
PointerGetDatum(name),
ObjectIdGetDatum(namespaceId)))
{
/* Found something else first in path */
break ;
}
}
}
ReleaseSysCache(tup);
return visible;
}
/*
* DeconstructQualifiedName
* Given a possibly - qualified name expressed as a list of String nodes ,
* extract the schema name and object name .
*
* * nspname_p is set to NULL if there is no explicit schema name .
*/
void
DeconstructQualifiedName(const List *names,
char **nspname_p,
char **objname_p)
{
char *catalogname;
char *schemaname = NULL;
char *objname = NULL;
switch (list_length(names))
{
case 1 :
objname = strVal(linitial(names));
break ;
case 2 :
schemaname = strVal(linitial(names));
objname = strVal(lsecond(names));
break ;
case 3 :
catalogname = strVal(linitial(names));
schemaname = strVal(lsecond(names));
objname = strVal(lthird(names));
/*
* We check the catalog name and then ignore it .
*/
if (strcmp(catalogname, get_database_name(MyDatabaseId)) != 0 )
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cross-database references are not implemented: %s" ,
NameListToString(names))));
break ;
default :
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("improper qualified name (too many dotted names): %s" ,
NameListToString(names))));
break ;
}
*nspname_p = schemaname;
*objname_p = objname;
}
/*
* LookupNamespaceNoError
* Look up a schema name .
*
* Returns the namespace OID , or InvalidOid if not found .
*
* Note this does NOT perform any permissions check - - - callers are
* responsible for being sure that an appropriate check is made .
* In the majority of cases LookupExplicitNamespace is preferable .
*/
Oid
LookupNamespaceNoError(const char *nspname)
{
/* check for pg_temp alias */
if (strcmp(nspname, "pg_temp" ) == 0 )
{
if (OidIsValid(myTempNamespace))
{
InvokeNamespaceSearchHook(myTempNamespace, true );
return myTempNamespace;
}
/*
* Since this is used only for looking up existing objects , there is
* no point in trying to initialize the temp namespace here ; and doing
* so might create problems for some callers . Just report " not found " .
*/
return InvalidOid;
}
return get_namespace_oid(nspname, true );
}
/*
* LookupExplicitNamespace
* Process an explicitly - specified schema name : look up the schema
* and verify we have USAGE ( lookup ) rights in it .
*
* Returns the namespace OID
*/
Oid
LookupExplicitNamespace(const char *nspname, bool missing_ok)
{
Oid namespaceId;
AclResult aclresult;
/* check for pg_temp alias */
if (strcmp(nspname, "pg_temp" ) == 0 )
{
if (OidIsValid(myTempNamespace))
return myTempNamespace;
/*
* Since this is used only for looking up existing objects , there is
* no point in trying to initialize the temp namespace here ; and doing
* so might create problems for some callers - - - just fall through .
*/
}
namespaceId = get_namespace_oid(nspname, missing_ok);
if (missing_ok && !OidIsValid(namespaceId))
return InvalidOid;
aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
nspname);
/* Schema search hook for this lookup */
InvokeNamespaceSearchHook(namespaceId, true );
return namespaceId;
}
/*
* LookupCreationNamespace
* Look up the schema and verify we have CREATE rights on it .
*
* This is just like LookupExplicitNamespace except for the different
* permission check , and that we are willing to create pg_temp if needed .
*
* Note : calling this may result in a CommandCounterIncrement operation ,
* if we have to create or clean out the temp namespace .
*/
Oid
LookupCreationNamespace(const char *nspname)
{
Oid namespaceId;
AclResult aclresult;
/* check for pg_temp alias */
if (strcmp(nspname, "pg_temp" ) == 0 )
{
/* Initialize temp namespace */
AccessTempTableNamespace(false );
return myTempNamespace;
}
namespaceId = get_namespace_oid(nspname, false );
aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
nspname);
return namespaceId;
}
/*
* Common checks on switching namespaces .
*
* We complain if either the old or new namespaces is a temporary schema
* ( or temporary toast schema ) , or if either the old or new namespaces is the
* TOAST schema .
*/
void
CheckSetNamespace(Oid oldNspOid, Oid nspOid)
{
/* disallow renaming into or out of temp schemas */
if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot move objects into or out of temporary schemas" )));
/* same for TOAST schema */
if (nspOid == PG_TOAST_NAMESPACE || oldNspOid == PG_TOAST_NAMESPACE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot move objects into or out of TOAST schema" )));
}
/*
* QualifiedNameGetCreationNamespace
* Given a possibly - qualified name for an object ( in List - of - Strings
* format ) , determine what namespace the object should be created in .
* Also extract and return the object name ( last component of list ) .
*
* Note : this does not apply any permissions check . Callers must check
* for CREATE rights on the selected namespace when appropriate .
*
* Note : calling this may result in a CommandCounterIncrement operation ,
* if we have to create or clean out the temp namespace .
*/
Oid
QualifiedNameGetCreationNamespace(const List *names, char **objname_p)
{
char *schemaname;
Oid namespaceId;
/* deconstruct the name list */
DeconstructQualifiedName(names, &schemaname, objname_p);
if (schemaname)
{
/* check for pg_temp alias */
if (strcmp(schemaname, "pg_temp" ) == 0 )
{
/* Initialize temp namespace */
AccessTempTableNamespace(false );
return myTempNamespace;
}
/* use exact schema given */
namespaceId = get_namespace_oid(schemaname, false );
/* we do not check for USAGE rights here! */
}
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" )));
}
return namespaceId;
}
/*
* get_namespace_oid - given a namespace name , look up the OID
*
* If missing_ok is false , throw an error if namespace name not found . If
* true , just return InvalidOid .
*/
Oid
get_namespace_oid(const char *nspname, bool missing_ok)
{
Oid oid;
oid = GetSysCacheOid1(NAMESPACENAME, Anum_pg_namespace_oid,
CStringGetDatum(nspname));
if (!OidIsValid(oid) && !missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_SCHEMA),
errmsg("schema \" %s\" does not exist" , nspname)));
return oid;
}
/*
* makeRangeVarFromNameList
* Utility routine to convert a qualified - name list into RangeVar form .
*/
RangeVar *
makeRangeVarFromNameList(const List *names)
{
RangeVar *rel = makeRangeVar(NULL, NULL, -1 );
switch (list_length(names))
{
case 1 :
rel->relname = strVal(linitial(names));
break ;
case 2 :
rel->schemaname = strVal(linitial(names));
rel->relname = strVal(lsecond(names));
break ;
case 3 :
rel->catalogname = strVal(linitial(names));
rel->schemaname = strVal(lsecond(names));
rel->relname = strVal(lthird(names));
break ;
default :
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("improper relation name (too many dotted names): %s" ,
NameListToString(names))));
break ;
}
return rel;
}
/*
* NameListToString
* Utility routine to convert a qualified - name list into a string .
*
* This is used primarily to form error messages , and so we do not quote
* the list elements , for the sake of legibility .
*
* In most scenarios the list elements should always be String values ,
* but we also allow A_Star for the convenience of ColumnRef processing .
*/
char *
NameListToString(const List *names)
{
StringInfoData string;
ListCell *l;
initStringInfo(&string);
foreach(l, names)
{
Node *name = (Node *) lfirst(l);
if (l != list_head(names))
appendStringInfoChar(&string, '.' );
if (IsA(name, String))
appendStringInfoString(&string, strVal(name));
else if (IsA(name, A_Star))
appendStringInfoChar(&string, '*' );
else
elog(ERROR, "unexpected node type in name list: %d" ,
(int ) nodeTag(name));
}
return string.data;
}
/*
* NameListToQuotedString
* Utility routine to convert a qualified - name list into a string .
*
* Same as above except that names will be double - quoted where necessary ,
* so the string could be re - parsed ( eg , by textToQualifiedNameList ) .
*/
char *
NameListToQuotedString(const List *names)
{
StringInfoData string;
ListCell *l;
initStringInfo(&string);
foreach(l, names)
{
if (l != list_head(names))
appendStringInfoChar(&string, '.' );
appendStringInfoString(&string, quote_identifier(strVal(lfirst(l))));
}
return string.data;
}
/*
* isTempNamespace - is the given namespace my temporary - table namespace ?
*/
bool
isTempNamespace(Oid namespaceId)
{
if (OidIsValid(myTempNamespace) && myTempNamespace == namespaceId)
return true ;
return false ;
}
/*
* isTempToastNamespace - is the given namespace my temporary - toast - table
* namespace ?
*/
bool
isTempToastNamespace(Oid namespaceId)
{
if (OidIsValid(myTempToastNamespace) && myTempToastNamespace == namespaceId)
return true ;
return false ;
}
/*
* isTempOrTempToastNamespace - is the given namespace my temporary - table
* namespace or my temporary - toast - table namespace ?
*/
bool
isTempOrTempToastNamespace(Oid namespaceId)
{
if (OidIsValid(myTempNamespace) &&
(myTempNamespace == namespaceId || myTempToastNamespace == namespaceId))
return true ;
return false ;
}
/*
* isAnyTempNamespace - is the given namespace a temporary - table namespace
* ( either my own , or another backend ' s ) ? Temporary - toast - table namespaces
* are included , too .
*/
bool
isAnyTempNamespace(Oid namespaceId)
{
bool result;
char *nspname;
/* True if the namespace name starts with "pg_temp_" or "pg_toast_temp_" */
nspname = get_namespace_name(namespaceId);
if (!nspname)
return false ; /* no such namespace? */
result = (strncmp(nspname, "pg_temp_" , 8 ) == 0 ) ||
(strncmp(nspname, "pg_toast_temp_" , 14 ) == 0 );
pfree(nspname);
return result;
}
/*
* isOtherTempNamespace - is the given namespace some other backend ' s
* temporary - table namespace ( including temporary - toast - table namespaces ) ?
*
* Note : for most purposes in the C code , this function is obsolete . Use
* RELATION_IS_OTHER_TEMP ( ) instead to detect non - local temp relations .
*/
bool
isOtherTempNamespace(Oid namespaceId)
{
/* If it's my own temp namespace, say "false" */
if (isTempOrTempToastNamespace(namespaceId))
return false ;
/* Else, if it's any temp namespace, say "true" */
return isAnyTempNamespace(namespaceId);
}
/*
* checkTempNamespaceStatus - is the given namespace owned and actively used
* by a backend ?
*
* Note : this can be used while scanning relations in pg_class to detect
* orphaned temporary tables or namespaces with a backend connected to a
* given database . The result may be out of date quickly , so the caller
* must be careful how to handle this information .
*/
TempNamespaceStatus
checkTempNamespaceStatus(Oid namespaceId)
{
PGPROC *proc;
ProcNumber procNumber;
Assert(OidIsValid(MyDatabaseId));
procNumber = GetTempNamespaceProcNumber(namespaceId);
/* No such namespace, or its name shows it's not temp? */
if (procNumber == INVALID_PROC_NUMBER)
return TEMP_NAMESPACE_NOT_TEMP;
/* Is the backend alive? */
proc = ProcNumberGetProc(procNumber);
if (proc == NULL)
return TEMP_NAMESPACE_IDLE;
/* Is the backend connected to the same database we are looking at? */
if (proc->databaseId != MyDatabaseId)
return TEMP_NAMESPACE_IDLE;
/* Does the backend own the temporary namespace? */
if (proc->tempNamespaceId != namespaceId)
return TEMP_NAMESPACE_IDLE;
/* Yup, so namespace is busy */
return TEMP_NAMESPACE_IN_USE;
}
/*
* GetTempNamespaceProcNumber - if the given namespace is a temporary - table
* namespace ( either my own , or another backend ' s ) , return the proc number
* that owns it . Temporary - toast - table namespaces are included , too .
* If it isn ' t a temp namespace , return INVALID_PROC_NUMBER .
*/
ProcNumber
GetTempNamespaceProcNumber(Oid namespaceId)
{
int result;
char *nspname;
/* See if the namespace name starts with "pg_temp_" or "pg_toast_temp_" */
nspname = get_namespace_name(namespaceId);
if (!nspname)
return INVALID_PROC_NUMBER; /* no such namespace? */
if (strncmp(nspname, "pg_temp_" , 8 ) == 0 )
result = atoi(nspname + 8 );
else if (strncmp(nspname, "pg_toast_temp_" , 14 ) == 0 )
result = atoi(nspname + 14 );
else
result = INVALID_PROC_NUMBER;
pfree(nspname);
return result;
}
/*
* GetTempToastNamespace - get the OID of my temporary - toast - table namespace ,
* which must already be assigned . ( This is only used when creating a toast
* table for a temp table , so we must have already done InitTempTableNamespace )
*/
Oid
GetTempToastNamespace(void )
{
Assert(OidIsValid(myTempToastNamespace));
return myTempToastNamespace;
}
/*
* GetTempNamespaceState - fetch status of session ' s temporary namespace
*
* This is used for conveying state to a parallel worker , and is not meant
* for general - purpose access .
*/
void
GetTempNamespaceState(Oid *tempNamespaceId, Oid *tempToastNamespaceId)
{
/* Return namespace OIDs, or 0 if session has not created temp namespace */
*tempNamespaceId = myTempNamespace;
*tempToastNamespaceId = myTempToastNamespace;
}
/*
* SetTempNamespaceState - set status of session ' s temporary namespace
*
* This is used for conveying state to a parallel worker , and is not meant for
* general - purpose access . By transferring these namespace OIDs to workers ,
* we ensure they will have the same notion of the search path as their leader
* does .
*/
void
SetTempNamespaceState(Oid tempNamespaceId, Oid tempToastNamespaceId)
{
/* Worker should not have created its own namespaces ... */
Assert(myTempNamespace == InvalidOid);
Assert(myTempToastNamespace == InvalidOid);
Assert(myTempNamespaceSubID == InvalidSubTransactionId);
/* Assign same namespace OIDs that leader has */
myTempNamespace = tempNamespaceId;
myTempToastNamespace = tempToastNamespaceId;
/*
* It ' s fine to leave myTempNamespaceSubID = = InvalidSubTransactionId .
* Even if the namespace is new so far as the leader is concerned , it ' s
* not new to the worker , and we certainly wouldn ' t want the worker trying
* to destroy it .
*/
baseSearchPathValid = false ; /* may need to rebuild list */
searchPathCacheValid = false ;
}
/*
* GetSearchPathMatcher - fetch current search path definition .
*
* The result structure is allocated in the specified memory context
* ( which might or might not be equal to CurrentMemoryContext ) ; but any
* junk created by revalidation calculations will be in CurrentMemoryContext .
*/
SearchPathMatcher *
GetSearchPathMatcher(MemoryContext context)
{
SearchPathMatcher *result;
List *schemas;
MemoryContext oldcxt;
recomputeNamespacePath();
oldcxt = MemoryContextSwitchTo(context);
result = (SearchPathMatcher *) palloc0(sizeof (SearchPathMatcher));
schemas = list_copy(activeSearchPath);
while (schemas && linitial_oid(schemas) != activeCreationNamespace)
{
if (linitial_oid(schemas) == myTempNamespace)
result->addTemp = true ;
else
{
Assert(linitial_oid(schemas) == PG_CATALOG_NAMESPACE);
result->addCatalog = true ;
}
schemas = list_delete_first(schemas);
}
result->schemas = schemas;
result->generation = activePathGeneration;
MemoryContextSwitchTo(oldcxt);
return result;
}
/*
* CopySearchPathMatcher - copy the specified SearchPathMatcher .
*
* The result structure is allocated in CurrentMemoryContext .
*/
SearchPathMatcher *
CopySearchPathMatcher(SearchPathMatcher *path)
{
SearchPathMatcher *result;
result = (SearchPathMatcher *) palloc(sizeof (SearchPathMatcher));
result->schemas = list_copy(path->schemas);
result->addCatalog = path->addCatalog;
result->addTemp = path->addTemp;
result->generation = path->generation;
return result;
}
/*
* SearchPathMatchesCurrentEnvironment - does path match current environment ?
*
* This is tested over and over in some common code paths , and in the typical
* scenario where the active search path seldom changes , it ' ll always succeed .
* We make that case fast by keeping a generation counter that is advanced
* whenever the active search path changes .
*/
bool
SearchPathMatchesCurrentEnvironment(SearchPathMatcher *path)
{
ListCell *lc,
*lcp;
recomputeNamespacePath();
/* Quick out if already known equal to active path. */
if (path->generation == activePathGeneration)
return true ;
/* We scan down the activeSearchPath to see if it matches the input. */
lc = list_head(activeSearchPath);
/* If path->addTemp, first item should be my temp namespace. */
if (path->addTemp)
{
if (lc && lfirst_oid(lc) == myTempNamespace)
lc = lnext(activeSearchPath, lc);
else
return false ;
}
/* If path->addCatalog, next item should be pg_catalog. */
if (path->addCatalog)
{
if (lc && lfirst_oid(lc) == PG_CATALOG_NAMESPACE)
lc = lnext(activeSearchPath, lc);
else
return false ;
}
/* We should now be looking at the activeCreationNamespace. */
if (activeCreationNamespace != (lc ? lfirst_oid(lc) : InvalidOid))
return false ;
/* The remainder of activeSearchPath should match path->schemas. */
foreach(lcp, path->schemas)
{
if (lc && lfirst_oid(lc) == lfirst_oid(lcp))
lc = lnext(activeSearchPath, lc);
else
return false ;
}
if (lc)
return false ;
/*
* Update path - > generation so that future tests will return quickly , so
* long as the active search path doesn ' t change .
*/
path->generation = activePathGeneration;
return true ;
}
/*
* get_collation_oid - find a collation by possibly qualified name
*
* Note that this will only find collations that work with the current
* database ' s encoding .
*/
Oid
get_collation_oid(List *collname, bool missing_ok)
{
char *schemaname;
char *collation_name;
int32 dbencoding = GetDatabaseEncoding();
Oid namespaceId;
Oid colloid;
ListCell *l;
/* deconstruct the name list */
DeconstructQualifiedName(collname, &schemaname, &collation_name);
if (schemaname)
{
/* use exact schema given */
namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
if (missing_ok && !OidIsValid(namespaceId))
return InvalidOid;
colloid = lookup_collation(collation_name, namespaceId, dbencoding);
if (OidIsValid(colloid))
return colloid;
}
else
{
/* search for it in search path */
recomputeNamespacePath();
foreach(l, activeSearchPath)
{
namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
colloid = lookup_collation(collation_name, namespaceId, dbencoding);
if (OidIsValid(colloid))
return colloid;
}
}
/* Not found in path */
if (!missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("collation \" %s\" for encoding \" %s\" does not exist" ,
NameListToString(collname), GetDatabaseEncodingName())));
return InvalidOid;
}
/*
* get_conversion_oid - find a conversion by possibly qualified name
*/
Oid
get_conversion_oid(List *conname, bool missing_ok)
{
char *schemaname;
char *conversion_name;
Oid namespaceId;
Oid conoid = InvalidOid;
ListCell *l;
/* deconstruct the name list */
DeconstructQualifiedName(conname, &schemaname, &conversion_name);
if (schemaname)
{
/* use exact schema given */
namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
if (missing_ok && !OidIsValid(namespaceId))
conoid = InvalidOid;
else
conoid = GetSysCacheOid2(CONNAMENSP, Anum_pg_conversion_oid,
PointerGetDatum(conversion_name),
ObjectIdGetDatum(namespaceId));
}
else
{
/* search for it in search path */
recomputeNamespacePath();
foreach(l, activeSearchPath)
{
namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
conoid = GetSysCacheOid2(CONNAMENSP, Anum_pg_conversion_oid,
PointerGetDatum(conversion_name),
ObjectIdGetDatum(namespaceId));
if (OidIsValid(conoid))
return conoid;
}
}
/* Not found in path */
if (!OidIsValid(conoid) && !missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("conversion \" %s\" does not exist" ,
NameListToString(conname))));
return conoid;
}
/*
* FindDefaultConversionProc - find default encoding conversion proc
*/
Oid
FindDefaultConversionProc(int32 for_encoding, int32 to_encoding)
{
Oid proc;
ListCell *l;
recomputeNamespacePath();
foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue ; /* do not look in temp namespace */
proc = FindDefaultConversion(namespaceId, for_encoding, to_encoding);
if (OidIsValid(proc))
return proc;
}
/* Not found in path */
return InvalidOid;
}
/*
* Look up namespace IDs and perform ACL checks . Return newly - allocated list .
*/
static List *
preprocessNamespacePath(const char *searchPath, Oid roleid,
bool *temp_missing)
{
char *rawname;
List *namelist;
List *oidlist;
ListCell *l;
/* Need a modifiable copy */
rawname = pstrdup(searchPath);
/* 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;
*temp_missing = false ;
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 = 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);
if (OidIsValid(namespaceId) &&
object_aclcheck(NamespaceRelationId, namespaceId, roleid,
ACL_USAGE) == ACLCHECK_OK)
oidlist = lappend_oid(oidlist, namespaceId);
}
}
else if (strcmp(curname, "pg_temp" ) == 0 )
{
/* pg_temp --- substitute temp namespace, if any */
if (OidIsValid(myTempNamespace))
oidlist = lappend_oid(oidlist, myTempNamespace);
else
{
/* If it ought to be the creation namespace, set flag */
if (oidlist == NIL)
*temp_missing = true ;
}
}
else
{
/* normal namespace reference */
namespaceId = get_namespace_oid(curname, true );
if (OidIsValid(namespaceId) &&
object_aclcheck(NamespaceRelationId, namespaceId, roleid,
ACL_USAGE) == ACLCHECK_OK)
oidlist = lappend_oid(oidlist, namespaceId);
}
}
pfree(rawname);
list_free(namelist);
return oidlist;
}
/*
* Remove duplicates , run namespace search hooks , and prepend
* implicitly - searched namespaces . Return newly - allocated list .
*
* If an object_access_hook is present , this must always be recalculated . It
* may seem that duplicate elimination is not dependent on the result of the
* hook , but if a hook returns different results on different calls for the
* same namespace ID , then it could affect the order in which that namespace
* appears in the final list .
*/
static List *
finalNamespacePath(List *oidlist, Oid *firstNS)
{
List *finalPath = NIL;
ListCell *lc;
foreach(lc, oidlist)
{
Oid namespaceId = lfirst_oid(lc);
if (!list_member_oid(finalPath, namespaceId))
{
if (InvokeNamespaceSearchHook(namespaceId, false ))
finalPath = lappend_oid(finalPath, namespaceId);
}
}
/*
* Remember the first member of the explicit list . ( Note : this is
* nominally wrong if temp_missing , but we need it anyway to distinguish
* explicit from implicit mention of pg_catalog . )
*/
if (finalPath == NIL)
*firstNS = InvalidOid;
else
*firstNS = linitial_oid(finalPath);
/*
* 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(finalPath, PG_CATALOG_NAMESPACE))
finalPath = lcons_oid(PG_CATALOG_NAMESPACE, finalPath);
if (OidIsValid(myTempNamespace) &&
!list_member_oid(finalPath, myTempNamespace))
finalPath = lcons_oid(myTempNamespace, finalPath);
return finalPath;
}
/*
* Retrieve search path information from the cache ; or if not there , fill
* it . The returned entry is valid only until the next call to this function .
*/
static const SearchPathCacheEntry *
cachedNamespacePath(const char *searchPath, Oid roleid)
{
MemoryContext oldcxt;
SearchPathCacheEntry *entry;
spcache_init();
entry = spcache_insert(searchPath, roleid);
/*
* An OOM may have resulted in a cache entry with missing ' oidlist ' or
* ' finalPath ' , so just compute whatever is missing .
*/
if (entry->oidlist == NIL)
{
oldcxt = MemoryContextSwitchTo(SearchPathCacheContext);
entry->oidlist = preprocessNamespacePath(searchPath, roleid,
&entry->temp_missing);
MemoryContextSwitchTo(oldcxt);
}
/*
* If a hook is set , we must recompute finalPath from the oidlist each
* time , because the hook may affect the result . This is still much faster
* than recomputing from the string ( and doing catalog lookups and ACL
* checks ) .
*/
if (entry->finalPath == NIL || object_access_hook ||
entry->forceRecompute)
{
list_free(entry->finalPath);
entry->finalPath = NIL;
oldcxt = MemoryContextSwitchTo(SearchPathCacheContext);
entry->finalPath = finalNamespacePath(entry->oidlist,
&entry->firstNS);
MemoryContextSwitchTo(oldcxt);
/*
* If an object_access_hook is set when finalPath is calculated , the
* result may be affected by the hook . Force recomputation of
* finalPath the next time this cache entry is used , even if the
* object_access_hook is not set at that time .
*/
entry->forceRecompute = object_access_hook ? true : false ;
}
return entry;
}
/*
* recomputeNamespacePath - recompute path derived variables if needed .
*/
static void
recomputeNamespacePath(void )
{
Oid roleid = GetUserId();
bool pathChanged;
const SearchPathCacheEntry *entry;
/* Do nothing if path is already valid. */
if (baseSearchPathValid && namespaceUser == roleid)
return ;
entry = cachedNamespacePath(namespace_search_path, roleid);
if (baseCreationNamespace == entry->firstNS &&
baseTempCreationPending == entry->temp_missing &&
equal(entry->finalPath, baseSearchPath))
{
pathChanged = false ;
}
else
{
MemoryContext oldcxt;
List *newpath;
pathChanged = true ;
/* Must save OID list in permanent storage. */
oldcxt = MemoryContextSwitchTo(TopMemoryContext);
newpath = list_copy(entry->finalPath);
MemoryContextSwitchTo(oldcxt);
/* Now safe to assign to state variables. */
list_free(baseSearchPath);
baseSearchPath = newpath;
baseCreationNamespace = entry->firstNS;
baseTempCreationPending = entry->temp_missing;
}
/* Mark the path valid. */
baseSearchPathValid = true ;
namespaceUser = roleid;
/* And make it 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 . )
*/
if (pathChanged)
activePathGeneration++;
}
/*
* AccessTempTableNamespace
* Provide access to a temporary namespace , potentially creating it
* if not present yet . This routine registers if the namespace gets
* in use in this transaction . ' force ' can be set to true to allow
* the caller to enforce the creation of the temporary namespace for
* use in this backend , which happens if its creation is pending .
*/
static void
AccessTempTableNamespace(bool force)
{
/*
* Make note that this temporary namespace has been accessed in this
* transaction .
*/
MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
/*
* If the caller attempting to access a temporary schema expects the
* creation of the namespace to be pending and should be enforced , then go
* through the creation .
*/
if (!force && OidIsValid(myTempNamespace))
return ;
/*
* The temporary tablespace does not exist yet and is wanted , so
* initialize it .
*/
InitTempTableNamespace();
}
/*
* InitTempTableNamespace
* Initialize temp table namespace on first use in a particular backend
*/
static void
InitTempTableNamespace(void )
{
char namespaceName[NAMEDATALEN];
Oid namespaceId;
Oid toastspaceId;
Assert(!OidIsValid(myTempNamespace));
/*
* 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 (object_aclcheck(DatabaseRelationId, 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))));
/*
* Do not allow a Hot Standby session to make temp tables . Aside from
* problems with modifying the system catalogs , there is a naming
* conflict : pg_temp_N belongs to the session with proc number N on the
* primary , not to a hot standby session with the same proc number . We
* should not be able to get here anyway due to XactReadOnly checks , but
* let ' s just make real sure . Note that this also backstops various
* operations that allow XactReadOnly transactions to modify temp tables ;
* they ' d need RecoveryInProgress checks if not for this .
*/
if (RecoveryInProgress())
ereport(ERROR,
(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
errmsg("cannot create temporary tables during recovery" )));
/* Parallel workers can't create temporary tables, either. */
if (IsParallelWorker())
ereport(ERROR,
(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
errmsg("cannot create temporary tables during a parallel operation" )));
snprintf(namespaceName, sizeof (namespaceName), "pg_temp_%d" , MyProcNumber);
namespaceId = get_namespace_oid(namespaceName, true );
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,
true );
/* 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);
}
/*
* If the corresponding toast - table namespace doesn ' t exist yet , create
* it . ( We assume there is no need to clean it out if it does exist , since
* dropping a parent table should make its toast table go away . )
*/
snprintf(namespaceName, sizeof (namespaceName), "pg_toast_temp_%d" ,
MyProcNumber);
toastspaceId = get_namespace_oid(namespaceName, true );
if (!OidIsValid(toastspaceId))
{
toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID,
true );
/* Advance command counter to make namespace visible */
CommandCounterIncrement();
}
/*
* 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;
myTempToastNamespace = toastspaceId;
/*
* Mark MyProc as owning this namespace which other processes can use to
* decide if a temporary namespace is in use or not . We assume that
* assignment of namespaceId is an atomic operation . Even if it is not ,
* the temporary relation which resulted in the creation of this temporary
* namespace is still locked until the current transaction commits , and
* its pg_namespace row is not visible yet . However it does not matter :
* this flag makes the namespace as being in use , so no objects created on
* it would be removed concurrently .
*/
MyProc->tempNamespaceId = namespaceId;
/* It should not be done already. */
Assert(myTempNamespaceSubID == InvalidSubTransactionId);
myTempNamespaceSubID = GetCurrentSubTransactionId();
baseSearchPathValid = false ; /* need to rebuild list */
searchPathCacheValid = false ;
}
/*
* End - of - transaction cleanup for namespaces .
*/
void
AtEOXact_Namespace(bool isCommit, bool parallel)
{
/*
* 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 && !parallel)
{
if (isCommit)
before_shmem_exit(RemoveTempRelationsCallback, 0 );
else
{
myTempNamespace = InvalidOid;
myTempToastNamespace = InvalidOid;
baseSearchPathValid = false ; /* need to rebuild list */
searchPathCacheValid = false ;
/*
* Reset the temporary namespace flag in MyProc . We assume that
* this operation is atomic .
*
* Because this transaction is aborting , the pg_namespace row is
* not visible to anyone else anyway , but that doesn ' t matter :
* it ' s not a problem if objects contained in this namespace are
* removed concurrently .
*/
MyProc->tempNamespaceId = InvalidOid;
}
myTempNamespaceSubID = InvalidSubTransactionId;
}
}
/*
* 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 .
*/
void
AtEOSubXact_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;
myTempToastNamespace = InvalidOid;
baseSearchPathValid = false ; /* need to rebuild list */
searchPathCacheValid = false ;
/*
* Reset the temporary namespace flag in MyProc . We assume that
* this operation is atomic .
*
* Because this subtransaction is aborting , the pg_namespace row
* is not visible to anyone else anyway , but that doesn ' t matter :
* it ' s not a problem if objects contained in this namespace are
* removed concurrently .
*/
MyProc->tempNamespaceId = InvalidOid;
}
}
}
/*
* 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 void
RemoveTempRelations(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 ) . Hence , specify SKIP_ORIGINAL . It ' s also an INTERNAL
* deletion , and we want to not drop any extensions that might happen to
* own temp objects .
*/
object.classId = NamespaceRelationId;
object.objectId = tempNamespaceId;
object.objectSubId = 0 ;
performDeletion(&object, DROP_CASCADE,
PERFORM_DELETION_INTERNAL |
PERFORM_DELETION_QUIETLY |
PERFORM_DELETION_SKIP_ORIGINAL |
PERFORM_DELETION_SKIP_EXTENSIONS);
}
/*
* Callback to remove temp relations at backend exit .
*/
static void
RemoveTempRelationsCallback(int code, Datum arg)
{
if (OidIsValid(myTempNamespace)) /* should always be true */
{
/* Need to ensure we have a usable transaction. */
AbortOutOfAnyTransaction();
StartTransactionCommand();
PushActiveSnapshot(GetTransactionSnapshot());
RemoveTempRelations(myTempNamespace);
PopActiveSnapshot();
CommitTransactionCommand();
}
}
/*
* Remove all temp tables from the temporary namespace .
*/
void
ResetTempTableNamespace(void )
{
if (OidIsValid(myTempNamespace))
RemoveTempRelations(myTempNamespace);
}
/*
* Routines for handling the GUC variable ' search_path ' .
*/
/* check_hook: validate new search_path value */
bool
check_search_path(char **newval, void **extra, GucSource source)
{
Oid roleid = InvalidOid;
const char *searchPath = *newval;
char *rawname;
List *namelist;
bool use_cache = (SearchPathCacheContext != NULL);
/*
* We used to try to check that the named schemas exist , but there are
* many valid use - cases for having search_path settings that include
* schemas that don ' t exist ; and often , we are not inside a transaction
* here and so can ' t consult the system catalogs anyway . So now , the only
* requirement is syntactic validity of the identifier list .
*
* Checking only the syntactic validity also allows us to use the search
* path cache ( if available ) to avoid calling SplitIdentifierString ( ) on
* the same string repeatedly .
*/
if (use_cache)
{
spcache_init();
roleid = GetUserId();
if (spcache_lookup(searchPath, roleid) != NULL)
return true ;
}
/*
* Ensure validity check succeeds before creating cache entry .
*/
rawname = pstrdup(searchPath); /* need a modifiable copy */
/* Parse string into list of identifiers */
if (!SplitIdentifierString(rawname, ',' , &namelist))
{
/* syntax error in name list */
GUC_check_errdetail("List syntax is invalid." );
pfree(rawname);
list_free(namelist);
return false ;
}
pfree(rawname);
list_free(namelist);
/* OK to create empty cache entry */
if (use_cache)
(void ) spcache_insert(searchPath, roleid);
return true ;
}
/* assign_hook: do extra actions as needed */
void
assign_search_path(const char *newval, void *extra)
{
/* don't access search_path during bootstrap */
Assert(!IsBootstrapProcessingMode());
/*
* 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 , or outside a transaction .
*
* This does not invalidate the search path cache , so if this value had
* been previously set and no syscache invalidations happened ,
* recomputation may not be necessary .
*/
baseSearchPathValid = false ;
}
/*
* InitializeSearchPath : initialize module during InitPostgres .
*
* This is called after we are up enough to be able to do catalog lookups .
*/
void
InitializeSearchPath(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);
baseSearchPath = list_make1_oid(PG_CATALOG_NAMESPACE);
MemoryContextSwitchTo(oldcxt);
baseCreationNamespace = PG_CATALOG_NAMESPACE;
baseTempCreationPending = false ;
baseSearchPathValid = true ;
namespaceUser = GetUserId();
activeSearchPath = baseSearchPath;
activeCreationNamespace = baseCreationNamespace;
activeTempCreationPending = baseTempCreationPending;
activePathGeneration++; /* pro forma */
}
else
{
/*
* In normal mode , arrange for a callback on any syscache invalidation
* that will affect the search_path cache .
*/
/* namespace name or ACLs may have changed */
CacheRegisterSyscacheCallback(NAMESPACEOID,
InvalidationCallback,
(Datum) 0 );
/* role name may affect the meaning of "$user" */
CacheRegisterSyscacheCallback(AUTHOID,
InvalidationCallback,
(Datum) 0 );
/* role membership may affect ACLs */
CacheRegisterSyscacheCallback(AUTHMEMROLEMEM,
InvalidationCallback,
(Datum) 0 );
/* database owner may affect ACLs */
CacheRegisterSyscacheCallback(DATABASEOID,
InvalidationCallback,
(Datum) 0 );
/* Force search path to be recomputed on next use */
baseSearchPathValid = false ;
searchPathCacheValid = false ;
}
}
/*
* InvalidationCallback
* Syscache inval callback function
*/
static void
InvalidationCallback(Datum arg, int cacheid, uint32 hashvalue)
{
/*
* Force search path to be recomputed on next use , also invalidating the
* search path cache ( because namespace names , ACLs , or role names may
* have changed ) .
*/
baseSearchPathValid = false ;
searchPathCacheValid = 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 .
*
* Note : calling this may result in a CommandCounterIncrement operation ,
* if we have to create or clean out the temp namespace .
*/
List *
fetch_search_path(bool includeImplicit)
{
List *result;
recomputeNamespacePath();
/*
* If the temp namespace should be first , force it to exist . This is so
* that callers can trust the result to reflect the actual default
* creation namespace . It ' s a bit bogus to do this here , since
* current_schema ( ) is supposedly a stable function without side - effects ,
* but the alternatives seem worse .
*/
if (activeTempCreationPending)
{
AccessTempTableNamespace(true );
recomputeNamespacePath();
}
result = list_copy(activeSearchPath);
if (!includeImplicit)
{
while (result && linitial_oid(result) != activeCreationNamespace)
result = list_delete_first(result);
}
return result;
}
/*
* Fetch the active search path into a caller - allocated array of OIDs .
* Returns the number of path entries . ( If this is more than sarray_len ,
* then the data didn ' t fit and is not all stored . )
*
* The returned list always includes the implicitly - prepended namespaces ,
* but never includes the temp namespace . ( This is suitable for existing
* users , which would want to ignore the temp namespace anyway . ) This
* definition allows us to not worry about initializing the temp namespace .
*/
int
fetch_search_path_array(Oid *sarray, int sarray_len)
{
int count = 0 ;
ListCell *l;
recomputeNamespacePath();
foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue ; /* do not include temp namespace */
if (count < sarray_len)
sarray[count] = namespaceId;
count++;
}
return count;
}
/*
* Export the FooIsVisible functions as SQL - callable functions .
*
* Note : as of Postgres 8 . 4 , these will silently return NULL if called on
* a nonexistent object OID , rather than failing . This is to avoid race
* condition errors when a query that ' s scanning a catalog using an MVCC
* snapshot uses one of these functions . The underlying IsVisible functions
* always use an up - to - date snapshot and so might see the object as already
* gone when it ' s still visible to the transaction snapshot .
*/
Datum
pg_table_is_visible(PG_FUNCTION_ARGS)
{
Oid oid = PG_GETARG_OID(0 );
bool result;
bool is_missing = false ;
result = RelationIsVisibleExt(oid, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
Datum
pg_type_is_visible(PG_FUNCTION_ARGS)
{
Oid oid = PG_GETARG_OID(0 );
bool result;
bool is_missing = false ;
result = TypeIsVisibleExt(oid, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
Datum
pg_function_is_visible(PG_FUNCTION_ARGS)
{
Oid oid = PG_GETARG_OID(0 );
bool result;
bool is_missing = false ;
result = FunctionIsVisibleExt(oid, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
Datum
pg_operator_is_visible(PG_FUNCTION_ARGS)
{
Oid oid = PG_GETARG_OID(0 );
bool result;
bool is_missing = false ;
result = OperatorIsVisibleExt(oid, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
Datum
pg_opclass_is_visible(PG_FUNCTION_ARGS)
{
Oid oid = PG_GETARG_OID(0 );
bool result;
bool is_missing = false ;
result = OpclassIsVisibleExt(oid, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
Datum
pg_opfamily_is_visible(PG_FUNCTION_ARGS)
{
Oid oid = PG_GETARG_OID(0 );
bool result;
bool is_missing = false ;
result = OpfamilyIsVisibleExt(oid, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
Datum
pg_collation_is_visible(PG_FUNCTION_ARGS)
{
Oid oid = PG_GETARG_OID(0 );
bool result;
bool is_missing = false ;
result = CollationIsVisibleExt(oid, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
Datum
pg_conversion_is_visible(PG_FUNCTION_ARGS)
{
Oid oid = PG_GETARG_OID(0 );
bool result;
bool is_missing = false ;
result = ConversionIsVisibleExt(oid, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
Datum
pg_statistics_obj_is_visible(PG_FUNCTION_ARGS)
{
Oid oid = PG_GETARG_OID(0 );
bool result;
bool is_missing = false ;
result = StatisticsObjIsVisibleExt(oid, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
Datum
pg_ts_parser_is_visible(PG_FUNCTION_ARGS)
{
Oid oid = PG_GETARG_OID(0 );
bool result;
bool is_missing = false ;
result = TSParserIsVisibleExt(oid, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
Datum
pg_ts_dict_is_visible(PG_FUNCTION_ARGS)
{
Oid oid = PG_GETARG_OID(0 );
bool result;
bool is_missing = false ;
result = TSDictionaryIsVisibleExt(oid, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
Datum
pg_ts_template_is_visible(PG_FUNCTION_ARGS)
{
Oid oid = PG_GETARG_OID(0 );
bool result;
bool is_missing = false ;
result = TSTemplateIsVisibleExt(oid, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
Datum
pg_ts_config_is_visible(PG_FUNCTION_ARGS)
{
Oid oid = PG_GETARG_OID(0 );
bool result;
bool is_missing = false ;
result = TSConfigIsVisibleExt(oid, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
Datum
pg_my_temp_schema(PG_FUNCTION_ARGS)
{
PG_RETURN_OID(myTempNamespace);
}
Datum
pg_is_other_temp_schema(PG_FUNCTION_ARGS)
{
Oid oid = PG_GETARG_OID(0 );
PG_RETURN_BOOL(isOtherTempNamespace(oid));
}
Messung V0.5 in Prozent C=93 H=95 G=93
[Verzeichnis aufwärts0.220unsichere VerbindungÜbersetzung europäischer Sprachen durch Browser2026-08-08]
2026-08-25
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=141584
#Domains=752002