@ -13,7 +13,7 @@
* Portions Copyright ( c ) 1994 , Regents of the University of California
*
* IDENTIFICATION
* $ PostgreSQL : pgsql / src / backend / catalog / namespace . c , v 1.95 2007 / 04 / 12 22 : 34 : 45 neilc Exp $
* $ PostgreSQL : pgsql / src / backend / catalog / namespace . c , v 1.96 2007 / 04 / 20 02 : 37 : 37 tgl Exp $
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
@ -62,12 +62,30 @@
* 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 .
*
* In bootstrap mode , the search path is set equal to ' pg_catalog ' , so that
* 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
* 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
@ -102,15 +120,20 @@ 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 ;
/* 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 three values are valid only if baseSearchPathValid */
/* The above four values are valid only if baseSearchPathValid */
static bool baseSearchPathValid = true ;
/* Override requests are remembered in a stack of OverrideStackEntry structs */
@ -262,6 +285,14 @@ RangeVarGetCreationNamespace(const RangeVar *newRelation)
if ( newRelation - > schemaname )
{
/* check for pg_temp alias */
if ( strcmp ( newRelation - > schemaname , " pg_temp " ) = = 0 )
{
/* Initialize temp namespace if first time through */
if ( ! OidIsValid ( myTempNamespace ) )
InitTempTableNamespace ( ) ;
return myTempNamespace ;
}
/* use exact schema given */
namespaceId = GetSysCacheOid ( NAMESPACENAME ,
CStringGetDatum ( newRelation - > schemaname ) ,
@ -277,6 +308,12 @@ RangeVarGetCreationNamespace(const RangeVar *newRelation)
{
/* use the default creation namespace */
recomputeNamespacePath ( ) ;
if ( activeTempCreationPending )
{
/* Need to initialize temp namespace */
InitTempTableNamespace ( ) ;
return myTempNamespace ;
}
namespaceId = activeCreationNamespace ;
if ( ! OidIsValid ( namespaceId ) )
ereport ( ERROR ,
@ -549,12 +586,16 @@ FuncnameGetCandidates(List *names, int nargs)
}
else
{
/* Consider only procs that are in the search path */
/*
* 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 ) )
if ( procform - > pronamespace = = lfirst_oid ( nsp ) & &
procform - > pronamespace ! = myTempNamespace )
break ;
pathpos + + ;
}
@ -770,6 +811,9 @@ OpernameGetOprid(List *names, Oid oprleft, Oid oprright)
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 ;
@ -872,12 +916,16 @@ OpernameGetCandidates(List *names, char oprkind)
}
else
{
/* Consider only opers that are in the search path */
/*
* 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 ) )
if ( operform - > oprnamespace = = lfirst_oid ( nsp ) & &
operform - > oprnamespace ! = myTempNamespace )
break ;
pathpos + + ;
}
@ -1025,6 +1073,9 @@ OpclassnameGetOpcid(Oid amid, const char *opcname)
{
Oid namespaceId = lfirst_oid ( l ) ;
if ( namespaceId = = myTempNamespace )
continue ; /* do not look in temp namespace */
opcid = GetSysCacheOid ( CLAAMNAMENSP ,
ObjectIdGetDatum ( amid ) ,
PointerGetDatum ( opcname ) ,
@ -1108,6 +1159,9 @@ OpfamilynameGetOpfid(Oid amid, const char *opfname)
{
Oid namespaceId = lfirst_oid ( l ) ;
if ( namespaceId = = myTempNamespace )
continue ; /* do not look in temp namespace */
opfid = GetSysCacheOid ( OPFAMILYAMNAMENSP ,
ObjectIdGetDatum ( amid ) ,
PointerGetDatum ( opfname ) ,
@ -1190,6 +1244,9 @@ ConversionGetConid(const char *conname)
{
Oid namespaceId = lfirst_oid ( l ) ;
if ( namespaceId = = myTempNamespace )
continue ; /* do not look in temp namespace */
conid = GetSysCacheOid ( CONNAMENSP ,
PointerGetDatum ( conname ) ,
ObjectIdGetDatum ( namespaceId ) ,
@ -1316,6 +1373,19 @@ LookupExplicitNamespace(const char *nspname)
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 and give the " does not exist " error .
*/
}
namespaceId = GetSysCacheOid ( NAMESPACENAME ,
CStringGetDatum ( nspname ) ,
0 , 0 , 0 ) ;
@ -1336,7 +1406,11 @@ LookupExplicitNamespace(const char *nspname)
* LookupCreationNamespace
* Look up the schema and verify we have CREATE rights on it .
*
* This is just like LookupExplicitNamespace except for the permission check .
* This is just like LookupExplicitNamespace except for the 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 )
@ -1344,6 +1418,15 @@ LookupCreationNamespace(const char *nspname)
Oid namespaceId ;
AclResult aclresult ;
/* check for pg_temp alias */
if ( strcmp ( nspname , " pg_temp " ) = = 0 )
{
/* Initialize temp namespace if first time through */
if ( ! OidIsValid ( myTempNamespace ) )
InitTempTableNamespace ( ) ;
return myTempNamespace ;
}
namespaceId = GetSysCacheOid ( NAMESPACENAME ,
CStringGetDatum ( nspname ) ,
0 , 0 , 0 ) ;
@ -1369,21 +1452,28 @@ LookupCreationNamespace(const char *nspname)
* Note : this does not apply any permissions check . Callers must check
* for CREATE rights on the selected namespace when appropriate .
*
* This is * not * used for tables . Hence , the TEMP table namespace is
* never selected as the creation target .
* Note : calling this may result in a CommandCounterIncrement operation ,
* if we have to create or clean out the temp namespace .
*/
Oid
QualifiedNameGetCreationNamespace ( List * names , char * * objname_p )
{
char * schemaname ;
char * objname ;
Oid namespaceId ;
/* deconstruct the name list */
DeconstructQualifiedName ( names , & schemaname , & objname ) ;
DeconstructQualifiedName ( names , & schemaname , objname_p ) ;
if ( schemaname )
{
/* check for pg_temp alias */
if ( strcmp ( schemaname , " pg_temp " ) = = 0 )
{
/* Initialize temp namespace if first time through */
if ( ! OidIsValid ( myTempNamespace ) )
InitTempTableNamespace ( ) ;
return myTempNamespace ;
}
/* use exact schema given */
namespaceId = GetSysCacheOid ( NAMESPACENAME ,
CStringGetDatum ( schemaname ) ,
@ -1398,6 +1488,12 @@ QualifiedNameGetCreationNamespace(List *names, char **objname_p)
{
/* use the default creation namespace */
recomputeNamespacePath ( ) ;
if ( activeTempCreationPending )
{
/* Need to initialize temp namespace */
InitTempTableNamespace ( ) ;
return myTempNamespace ;
}
namespaceId = activeCreationNamespace ;
if ( ! OidIsValid ( namespaceId ) )
ereport ( ERROR ,
@ -1405,7 +1501,6 @@ QualifiedNameGetCreationNamespace(List *names, char **objname_p)
errmsg ( " no schema has been selected to create in " ) ) ) ;
}
* objname_p = objname ;
return namespaceId ;
}
@ -1634,6 +1729,7 @@ PushOverrideSearchPath(OverrideSearchPath *newpath)
/* And make it active. */
activeSearchPath = entry - > searchPath ;
activeCreationNamespace = entry - > creationNamespace ;
activeTempCreationPending = false ; /* XXX is this OK? */
MemoryContextSwitchTo ( oldcxt ) ;
}
@ -1667,12 +1763,14 @@ PopOverrideSearchPath(void)
entry = ( OverrideStackEntry * ) linitial ( overrideStack ) ;
activeSearchPath = entry - > searchPath ;
activeCreationNamespace = entry - > creationNamespace ;
activeTempCreationPending = false ; /* XXX is this OK? */
}
else
{
/* If not baseSearchPathValid, this is useless but harmless */
activeSearchPath = baseSearchPath ;
activeCreationNamespace = baseCreationNamespace ;
activeTempCreationPending = baseTempCreationPending ;
}
}
@ -1706,6 +1804,10 @@ FindConversionByName(List *name)
foreach ( l , activeSearchPath )
{
namespaceId = lfirst_oid ( l ) ;
if ( namespaceId = = myTempNamespace )
continue ; /* do not look in temp namespace */
conoid = FindConversion ( conversion_name , namespaceId ) ;
if ( OidIsValid ( conoid ) )
return conoid ;
@ -1731,6 +1833,9 @@ FindDefaultConversionProc(int4 for_encoding, int4 to_encoding)
{
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 ;
@ -1752,6 +1857,7 @@ recomputeNamespacePath(void)
List * oidlist ;
List * newpath ;
ListCell * l ;
bool temp_missing ;
Oid firstNS ;
MemoryContext oldcxt ;
@ -1781,6 +1887,7 @@ recomputeNamespacePath(void)
* already been accepted . ) Don ' t make duplicate entries , either .
*/
oidlist = NIL ;
temp_missing = false ;
foreach ( l , namelist )
{
char * curname = ( char * ) lfirst ( l ) ;
@ -1810,6 +1917,21 @@ recomputeNamespacePath(void)
oidlist = lappend_oid ( oidlist , namespaceId ) ;
}
}
else if ( strcmp ( curname , " pg_temp " ) = = 0 )
{
/* pg_temp --- substitute temp namespace, if any */
if ( OidIsValid ( myTempNamespace ) )
{
if ( ! list_member_oid ( oidlist , 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 */
@ -1825,7 +1947,9 @@ recomputeNamespacePath(void)
}
/*
* Remember the first member of the explicit list .
* 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 ( oidlist = = NIL )
firstNS = InvalidOid ;
@ -1856,6 +1980,7 @@ recomputeNamespacePath(void)
list_free ( baseSearchPath ) ;
baseSearchPath = newpath ;
baseCreationNamespace = firstNS ;
baseTempCreationPending = temp_missing ;
/* Mark the path valid. */
baseSearchPathValid = true ;
@ -1864,6 +1989,7 @@ recomputeNamespacePath(void)
/* And make it active. */
activeSearchPath = baseSearchPath ;
activeCreationNamespace = baseCreationNamespace ;
activeTempCreationPending = baseTempCreationPending ;
/* Clean up. */
pfree ( rawname ) ;
@ -1881,6 +2007,8 @@ InitTempTableNamespace(void)
char namespaceName [ NAMEDATALEN ] ;
Oid namespaceId ;
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:
@ -1940,16 +2068,6 @@ InitTempTableNamespace(void)
baseSearchPathValid = false ; /* need to rebuild list */
}
/*
* Remove all temp tables from the temporary namespace .
*/
void
ResetTempTableNamespace ( void )
{
if ( OidIsValid ( myTempNamespace ) )
RemoveTempRelations ( myTempNamespace ) ;
}
/*
* End - of - transaction cleanup for namespaces .
*/
@ -1995,6 +2113,7 @@ AtEOXact_Namespace(bool isCommit)
/* If not baseSearchPathValid, this is useless but harmless */
activeSearchPath = baseSearchPath ;
activeCreationNamespace = baseCreationNamespace ;
activeTempCreationPending = baseTempCreationPending ;
}
}
@ -2046,12 +2165,14 @@ AtEOSubXact_Namespace(bool isCommit, SubTransactionId mySubid,
entry = ( OverrideStackEntry * ) linitial ( overrideStack ) ;
activeSearchPath = entry - > searchPath ;
activeCreationNamespace = entry - > creationNamespace ;
activeTempCreationPending = false ; /* XXX is this OK? */
}
else
{
/* If not baseSearchPathValid, this is useless but harmless */
activeSearchPath = baseSearchPath ;
activeCreationNamespace = baseCreationNamespace ;
activeTempCreationPending = baseTempCreationPending ;
}
}
@ -2099,6 +2220,16 @@ RemoveTempRelationsCallback(int code, Datum arg)
}
}
/*
* Remove all temp tables from the temporary namespace .
*/
void
ResetTempTableNamespace ( void )
{
if ( OidIsValid ( myTempNamespace ) )
RemoveTempRelations ( myTempNamespace ) ;
}
/*
* Routines for handling the GUC variable ' search_path ' .
@ -2132,8 +2263,9 @@ assign_search_path(const char *newval, bool doit, GucSource source)
{
/*
* Verify that all the names are either valid namespace names or
* " $user " . We do not require $ user to correspond to a valid
* namespace . We do not check for USAGE rights , either ; should we ?
* " $user " or " pg_temp " . We do not require $ user to correspond to a
* valid namespace , and pg_temp might not exist yet . We do not check
* for USAGE rights , either ; should we ?
*
* When source = = PGC_S_TEST , we are checking the argument of an ALTER
* DATABASE SET or ALTER USER SET command . It could be that the
@ -2147,6 +2279,8 @@ assign_search_path(const char *newval, bool doit, GucSource source)
if ( strcmp ( curname , " $user " ) = = 0 )
continue ;
if ( strcmp ( curname , " pg_temp " ) = = 0 )
continue ;
if ( ! SearchSysCacheExists ( NAMESPACENAME ,
CStringGetDatum ( curname ) ,
0 , 0 , 0 ) )
@ -2190,10 +2324,12 @@ InitializeSearchPath(void)
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 ;
}
else
{
@ -2227,6 +2363,9 @@ NamespaceCallback(Datum arg, Oid relid)
*
* 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 )
@ -2235,6 +2374,19 @@ fetch_search_path(bool includeImplicit)
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 )
{
InitTempTableNamespace ( ) ;
recomputeNamespacePath ( ) ;
}
result = list_copy ( activeSearchPath ) ;
if ( ! includeImplicit )
{