mirror of
https://github.com/postgres/postgres.git
synced 2025-07-24 14:22:24 +03:00
Support explicit placement of the temporary-table schema within search_path.
This is needed to allow a security-definer function to set a truly secure value of search_path. Without it, a malicious user can use temporary objects to execute code with the privileges of the security-definer function. Even pushing the temp schema to the back of the search path is not quite good enough, because a function or operator at the back of the path might still capture control from one nearer the front due to having a more exact datatype match. Hence, disable searching the temp schema altogether for functions and operators. Security: CVE-2007-2138
This commit is contained in:
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.138 2007/03/26 16:58:38 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.139 2007/04/20 02:37:37 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* See acl.h.
|
||||
@ -1833,7 +1833,7 @@ pg_namespace_aclmask(Oid nsp_oid, Oid roleid,
|
||||
*/
|
||||
if (isTempNamespace(nsp_oid))
|
||||
{
|
||||
if (pg_database_aclcheck(MyDatabaseId, GetUserId(),
|
||||
if (pg_database_aclcheck(MyDatabaseId, roleid,
|
||||
ACL_CREATE_TEMP) == ACLCHECK_OK)
|
||||
return mask & ACL_ALL_RIGHTS_NAMESPACE;
|
||||
else
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -137,3 +137,61 @@ CREATE TEMP TABLE temptest4(col int REFERENCES temptest3);
|
||||
COMMIT;
|
||||
ERROR: unsupported ON COMMIT and foreign key combination
|
||||
DETAIL: Table "temptest4" references "temptest3", but they do not have the same ON COMMIT setting.
|
||||
-- Test manipulation of temp schema's placement in search path
|
||||
create table public.whereami (f1 text);
|
||||
insert into public.whereami values ('public');
|
||||
create temp table whereami (f1 text);
|
||||
insert into whereami values ('temp');
|
||||
create function public.whoami() returns text
|
||||
as $$select 'public'::text$$ language sql;
|
||||
create function pg_temp.whoami() returns text
|
||||
as $$select 'temp'::text$$ language sql;
|
||||
-- default should have pg_temp implicitly first, but only for tables
|
||||
select * from whereami;
|
||||
f1
|
||||
------
|
||||
temp
|
||||
(1 row)
|
||||
|
||||
select whoami();
|
||||
whoami
|
||||
--------
|
||||
public
|
||||
(1 row)
|
||||
|
||||
-- can list temp first explicitly, but it still doesn't affect functions
|
||||
set search_path = pg_temp, public;
|
||||
select * from whereami;
|
||||
f1
|
||||
------
|
||||
temp
|
||||
(1 row)
|
||||
|
||||
select whoami();
|
||||
whoami
|
||||
--------
|
||||
public
|
||||
(1 row)
|
||||
|
||||
-- or put it last for security
|
||||
set search_path = public, pg_temp;
|
||||
select * from whereami;
|
||||
f1
|
||||
--------
|
||||
public
|
||||
(1 row)
|
||||
|
||||
select whoami();
|
||||
whoami
|
||||
--------
|
||||
public
|
||||
(1 row)
|
||||
|
||||
-- you can invoke a temp function explicitly, though
|
||||
select pg_temp.whoami();
|
||||
whoami
|
||||
--------
|
||||
temp
|
||||
(1 row)
|
||||
|
||||
drop table public.whereami;
|
||||
|
@ -118,3 +118,36 @@ BEGIN;
|
||||
CREATE TEMP TABLE temptest3(col int PRIMARY KEY) ON COMMIT DELETE ROWS;
|
||||
CREATE TEMP TABLE temptest4(col int REFERENCES temptest3);
|
||||
COMMIT;
|
||||
|
||||
-- Test manipulation of temp schema's placement in search path
|
||||
|
||||
create table public.whereami (f1 text);
|
||||
insert into public.whereami values ('public');
|
||||
|
||||
create temp table whereami (f1 text);
|
||||
insert into whereami values ('temp');
|
||||
|
||||
create function public.whoami() returns text
|
||||
as $$select 'public'::text$$ language sql;
|
||||
|
||||
create function pg_temp.whoami() returns text
|
||||
as $$select 'temp'::text$$ language sql;
|
||||
|
||||
-- default should have pg_temp implicitly first, but only for tables
|
||||
select * from whereami;
|
||||
select whoami();
|
||||
|
||||
-- can list temp first explicitly, but it still doesn't affect functions
|
||||
set search_path = pg_temp, public;
|
||||
select * from whereami;
|
||||
select whoami();
|
||||
|
||||
-- or put it last for security
|
||||
set search_path = public, pg_temp;
|
||||
select * from whereami;
|
||||
select whoami();
|
||||
|
||||
-- you can invoke a temp function explicitly, though
|
||||
select pg_temp.whoami();
|
||||
|
||||
drop table public.whereami;
|
||||
|
Reference in New Issue
Block a user