* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.92 2007/02/14 01:58:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.93 2007/03/23 19:53:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* The namespace search path is a possibly-empty list of namespace OIDs.
- * In addition to the explicit list, several implicitly-searched namespaces
+ * In addition to the explicit list, implicitly-searched namespaces
* may be included:
*
- * 1. If a "special" namespace has been set by PushSpecialNamespace, it is
- * always searched first. (This is a hack for CREATE SCHEMA.)
+ * 1. If a TEMP table namespace has been initialized in this session, it
+ * is implicitly searched first. (The only time this doesn't happen is
+ * when we are obeying an override search path spec that says not to use the
+ * temp namespace, or the temp namespace is included in the explicit list.)
*
- * 2. If a TEMP table namespace has been initialized in this session, it
- * is always searched just after any special namespace.
- *
- * 3. The system catalog namespace is always searched. If the system
+ * 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
* SQL99. Also, this provides a way to search the system namespace first
* without thereby making it the default creation target namespace.)
*
- * The default creation target namespace is normally equal to the first
- * element of the explicit list, but is the "special" namespace when one
- * has been set. If the explicit list is empty and there is no special
- * namespace, there is no default target.
+ * 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 system namespace is the only one searched or inserted into.
- * The initdb script is also careful to set search_path to 'pg_catalog' for
- * its post-bootstrap standalone backend runs. Otherwise the default search
+ * 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).
*
- * If namespaceSearchPathValid is false, then namespaceSearchPath (and other
+ * We support a stack of "override" search path settings for use within
+ * specific sections of backend code. namespace_search_path is ignored
+ * whenever the override stack is nonempty. activeSearchPath is always
+ * the actually active path; it points either to the search list of the
+ * topmost stack entry, or 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.
* We mark it invalid upon an assignment to namespace_search_path or receipt
* of a syscache invalidation event for pg_namespace. The recomputation
- * is done during the next lookup attempt.
+ * is done during the next non-overridden lookup attempt. Note that an
+ * override spec is never subject to recomputation.
*
* Any namespaces mentioned in namespace_search_path that are not readable
- * by the current user ID are simply left out of namespaceSearchPath; so
+ * 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.
*/
-static List *namespaceSearchPath = NIL;
+/* These variables define the actually active state: */
-static Oid namespaceUser = InvalidOid;
+static List *activeSearchPath = NIL;
/* default place to create stuff; if InvalidOid, no default */
-static Oid defaultCreationNamespace = InvalidOid;
+static Oid activeCreationNamespace = InvalidOid;
-/* first explicit member of list; usually same as defaultCreationNamespace */
-static Oid firstExplicitNamespace = InvalidOid;
+/* These variables are the values last derived from namespace_search_path: */
-/* The above four values are valid only if namespaceSearchPathValid */
-static bool namespaceSearchPathValid = true;
+static List *baseSearchPath = NIL;
+
+static Oid baseCreationNamespace = InvalidOid;
+
+static Oid namespaceUser = InvalidOid;
+
+/* The above three values are valid only if baseSearchPathValid */
+static bool baseSearchPathValid = true;
+
+/* Override requests are remembered in a stack of OverrideStackEntry structs */
+
+typedef struct
+{
+ List *searchPath; /* the desired search path */
+ Oid creationNamespace; /* the desired creation namespace */
+ int nestLevel; /* subtransaction nesting level */
+} OverrideStackEntry;
+
+static List *overrideStack = NIL;
/*
* myTempNamespace is InvalidOid until and unless a TEMP namespace is set up
static SubTransactionId myTempNamespaceSubID = InvalidSubTransactionId;
/*
- * "Special" namespace for CREATE SCHEMA. If set, it's the first search
- * path element, and also the default creation namespace.
- */
-static Oid mySpecialNamespace = InvalidOid;
-
-/*
- * This is the text equivalent of the search path --- it's the value
+ * This is the user's textual search path specification --- it's the value
* of the GUC variable 'search_path'.
*/
char *namespace_search_path = NULL;
{
/* use the default creation namespace */
recomputeNamespacePath();
- namespaceId = defaultCreationNamespace;
+ namespaceId = activeCreationNamespace;
if (!OidIsValid(namespaceId))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_SCHEMA),
recomputeNamespacePath();
- foreach(l, namespaceSearchPath)
+ foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
*/
relnamespace = relform->relnamespace;
if (relnamespace != PG_CATALOG_NAMESPACE &&
- !list_member_oid(namespaceSearchPath, relnamespace))
+ !list_member_oid(activeSearchPath, relnamespace))
visible = false;
else
{
ListCell *l;
visible = false;
- foreach(l, namespaceSearchPath)
+ foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
recomputeNamespacePath();
- foreach(l, namespaceSearchPath)
+ foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
*/
typnamespace = typform->typnamespace;
if (typnamespace != PG_CATALOG_NAMESPACE &&
- !list_member_oid(namespaceSearchPath, typnamespace))
+ !list_member_oid(activeSearchPath, typnamespace))
visible = false;
else
{
ListCell *l;
visible = false;
- foreach(l, namespaceSearchPath)
+ foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
/* Consider only procs that are in the search path */
ListCell *nsp;
- foreach(nsp, namespaceSearchPath)
+ foreach(nsp, activeSearchPath)
{
if (procform->pronamespace == lfirst_oid(nsp))
break;
*/
pronamespace = procform->pronamespace;
if (pronamespace != PG_CATALOG_NAMESPACE &&
- !list_member_oid(namespaceSearchPath, pronamespace))
+ !list_member_oid(activeSearchPath, pronamespace))
visible = false;
else
{
*/
recomputeNamespacePath();
- foreach(l, namespaceSearchPath)
+ foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
int i;
/* Consider only opers that are in the search path */
ListCell *nsp;
- foreach(nsp, namespaceSearchPath)
+ foreach(nsp, activeSearchPath)
{
if (operform->oprnamespace == lfirst_oid(nsp))
break;
*/
oprnamespace = oprform->oprnamespace;
if (oprnamespace != PG_CATALOG_NAMESPACE &&
- !list_member_oid(namespaceSearchPath, oprnamespace))
+ !list_member_oid(activeSearchPath, oprnamespace))
visible = false;
else
{
recomputeNamespacePath();
- foreach(l, namespaceSearchPath)
+ foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
*/
opcnamespace = opcform->opcnamespace;
if (opcnamespace != PG_CATALOG_NAMESPACE &&
- !list_member_oid(namespaceSearchPath, opcnamespace))
+ !list_member_oid(activeSearchPath, opcnamespace))
visible = false;
else
{
recomputeNamespacePath();
- foreach(l, namespaceSearchPath)
+ foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
*/
opfnamespace = opfform->opfnamespace;
if (opfnamespace != PG_CATALOG_NAMESPACE &&
- !list_member_oid(namespaceSearchPath, opfnamespace))
+ !list_member_oid(activeSearchPath, opfnamespace))
visible = false;
else
{
recomputeNamespacePath();
- foreach(l, namespaceSearchPath)
+ foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
*/
connamespace = conform->connamespace;
if (connamespace != PG_CATALOG_NAMESPACE &&
- !list_member_oid(namespaceSearchPath, connamespace))
+ !list_member_oid(activeSearchPath, connamespace))
visible = false;
else
{
{
/* use the default creation namespace */
recomputeNamespacePath();
- namespaceId = defaultCreationNamespace;
+ namespaceId = activeCreationNamespace;
if (!OidIsValid(namespaceId))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_SCHEMA),
return isAnyTempNamespace(namespaceId);
}
+
/*
- * PushSpecialNamespace - push a "special" namespace onto the front of the
- * search path.
+ * GetOverrideSearchPath - fetch current search path definition in form
+ * used by PushOverrideSearchPath.
*
- * This is a slightly messy hack intended only for support of CREATE SCHEMA.
- * Although the API is defined to allow a stack of pushed namespaces, we
- * presently only support one at a time.
+ * 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.
+ */
+OverrideSearchPath *
+GetOverrideSearchPath(MemoryContext context)
+{
+ OverrideSearchPath *result;
+ List *schemas;
+ MemoryContext oldcxt;
+
+ recomputeNamespacePath();
+
+ oldcxt = MemoryContextSwitchTo(context);
+
+ result = (OverrideSearchPath *) palloc0(sizeof(OverrideSearchPath));
+ 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;
+
+ MemoryContextSwitchTo(oldcxt);
+
+ return result;
+}
+
+/*
+ * PushOverrideSearchPath - temporarily override the search path
*
- * The pushed namespace will be removed from the search path at end of
- * transaction, whether commit or abort.
+ * We allow nested overrides, hence the push/pop terminology. The GUC
+ * search_path variable is ignored while an override is active.
*/
void
-PushSpecialNamespace(Oid namespaceId)
+PushOverrideSearchPath(OverrideSearchPath *newpath)
{
- Assert(!OidIsValid(mySpecialNamespace));
- mySpecialNamespace = namespaceId;
- namespaceSearchPathValid = false;
+ OverrideStackEntry *entry;
+ List *oidlist;
+ Oid firstNS;
+ MemoryContext oldcxt;
+
+ /*
+ * Copy the list for safekeeping, and insert implicitly-searched
+ * namespaces as needed. This code should track recomputeNamespacePath.
+ */
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+
+ oidlist = list_copy(newpath->schemas);
+
+ /*
+ * Remember the first member of the explicit list.
+ */
+ if (oidlist == NIL)
+ firstNS = InvalidOid;
+ else
+ firstNS = linitial_oid(oidlist);
+
+ /*
+ * Add any implicitly-searched namespaces to the list. Note these go on
+ * the front, not the back; also notice that we do not check USAGE
+ * permissions for these.
+ */
+ if (newpath->addCatalog)
+ oidlist = lcons_oid(PG_CATALOG_NAMESPACE, oidlist);
+
+ if (newpath->addTemp)
+ {
+ Assert(OidIsValid(myTempNamespace));
+ oidlist = lcons_oid(myTempNamespace, oidlist);
+ }
+
+ /*
+ * Build the new stack entry, then insert it at the head of the list.
+ */
+ entry = (OverrideStackEntry *) palloc(sizeof(OverrideStackEntry));
+ entry->searchPath = oidlist;
+ entry->creationNamespace = firstNS;
+ entry->nestLevel = GetCurrentTransactionNestLevel();
+
+ overrideStack = lcons(entry, overrideStack);
+
+ /* And make it active. */
+ activeSearchPath = entry->searchPath;
+ activeCreationNamespace = entry->creationNamespace;
+
+ MemoryContextSwitchTo(oldcxt);
}
/*
- * PopSpecialNamespace - remove previously pushed special namespace.
+ * PopOverrideSearchPath - undo a previous PushOverrideSearchPath
+ *
+ * Any push during a (sub)transaction will be popped automatically at abort.
+ * But it's caller error if a push isn't popped in normal control flow.
*/
void
-PopSpecialNamespace(Oid namespaceId)
+PopOverrideSearchPath(void)
{
- Assert(mySpecialNamespace == namespaceId);
- mySpecialNamespace = InvalidOid;
- namespaceSearchPathValid = false;
+ OverrideStackEntry *entry;
+
+ /* Sanity checks. */
+ if (overrideStack == NIL)
+ elog(ERROR, "bogus PopOverrideSearchPath call");
+ entry = (OverrideStackEntry *) linitial(overrideStack);
+ if (entry->nestLevel != GetCurrentTransactionNestLevel())
+ elog(ERROR, "bogus PopOverrideSearchPath call");
+
+ /* Pop the stack and free storage. */
+ overrideStack = list_delete_first(overrideStack);
+ list_free(entry->searchPath);
+ pfree(entry);
+
+ /* Activate the next level down. */
+ if (overrideStack)
+ {
+ entry = (OverrideStackEntry *) linitial(overrideStack);
+ activeSearchPath = entry->searchPath;
+ activeCreationNamespace = entry->creationNamespace;
+ }
+ else
+ {
+ /* If not baseSearchPathValid, this is useless but harmless */
+ activeSearchPath = baseSearchPath;
+ activeCreationNamespace = baseCreationNamespace;
+ }
}
+
/*
* FindConversionByName - find a conversion by possibly qualified name
*/
/* search for it in search path */
recomputeNamespacePath();
- foreach(l, namespaceSearchPath)
+ foreach(l, activeSearchPath)
{
namespaceId = lfirst_oid(l);
conoid = FindConversion(conversion_name, namespaceId);
recomputeNamespacePath();
- foreach(l, namespaceSearchPath)
+ foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
Oid firstNS;
MemoryContext oldcxt;
- /*
- * Do nothing if path is already valid.
- */
- if (namespaceSearchPathValid && namespaceUser == roleid)
+ /* Do nothing if an override search spec is active. */
+ if (overrideStack)
+ return;
+
+ /* Do nothing if path is already valid. */
+ if (baseSearchPathValid && namespaceUser == roleid)
return;
/* Need a modifiable copy of namespace_search_path string */
!list_member_oid(oidlist, myTempNamespace))
oidlist = lcons_oid(myTempNamespace, oidlist);
- if (OidIsValid(mySpecialNamespace) &&
- !list_member_oid(oidlist, mySpecialNamespace))
- oidlist = lcons_oid(mySpecialNamespace, oidlist);
-
/*
* Now that we've successfully built the new list of namespace OIDs, save
* it in permanent storage.
newpath = list_copy(oidlist);
MemoryContextSwitchTo(oldcxt);
- /* Now safe to assign to state variable. */
- list_free(namespaceSearchPath);
- namespaceSearchPath = newpath;
-
- /*
- * Update info derived from search path.
- */
- firstExplicitNamespace = firstNS;
- if (OidIsValid(mySpecialNamespace))
- defaultCreationNamespace = mySpecialNamespace;
- else
- defaultCreationNamespace = firstNS;
+ /* Now safe to assign to state variables. */
+ list_free(baseSearchPath);
+ baseSearchPath = newpath;
+ baseCreationNamespace = firstNS;
/* Mark the path valid. */
- namespaceSearchPathValid = true;
+ baseSearchPathValid = true;
namespaceUser = roleid;
+ /* And make it active. */
+ activeSearchPath = baseSearchPath;
+ activeCreationNamespace = baseCreationNamespace;
+
/* Clean up. */
pfree(rawname);
list_free(namelist);
AssertState(myTempNamespaceSubID == InvalidSubTransactionId);
myTempNamespaceSubID = GetCurrentSubTransactionId();
- namespaceSearchPathValid = false; /* need to rebuild list */
+ baseSearchPathValid = false; /* need to rebuild list */
}
/*
else
{
myTempNamespace = InvalidOid;
- namespaceSearchPathValid = false; /* need to rebuild list */
+ baseSearchPathValid = false; /* need to rebuild list */
}
myTempNamespaceSubID = InvalidSubTransactionId;
}
/*
- * Clean up if someone failed to do PopSpecialNamespace
+ * Clean up if someone failed to do PopOverrideSearchPath
*/
- if (OidIsValid(mySpecialNamespace))
+ if (overrideStack)
{
- mySpecialNamespace = InvalidOid;
- namespaceSearchPathValid = false; /* need to rebuild list */
+ if (isCommit)
+ elog(WARNING, "leaked override search path");
+ while (overrideStack)
+ {
+ OverrideStackEntry *entry;
+
+ entry = (OverrideStackEntry *) linitial(overrideStack);
+ overrideStack = list_delete_first(overrideStack);
+ list_free(entry->searchPath);
+ pfree(entry);
+ }
+ /* If not baseSearchPathValid, this is useless but harmless */
+ activeSearchPath = baseSearchPath;
+ activeCreationNamespace = baseCreationNamespace;
}
}
AtEOSubXact_Namespace(bool isCommit, SubTransactionId mySubid,
SubTransactionId parentSubid)
{
+ OverrideStackEntry *entry;
+
if (myTempNamespaceSubID == mySubid)
{
if (isCommit)
myTempNamespaceSubID = InvalidSubTransactionId;
/* TEMP namespace creation failed, so reset state */
myTempNamespace = InvalidOid;
- namespaceSearchPathValid = false; /* need to rebuild list */
+ baseSearchPathValid = false; /* need to rebuild list */
}
}
+
+ /*
+ * Clean up if someone failed to do PopOverrideSearchPath
+ */
+ while (overrideStack)
+ {
+ entry = (OverrideStackEntry *) linitial(overrideStack);
+ if (entry->nestLevel < GetCurrentTransactionNestLevel())
+ break;
+ if (isCommit)
+ elog(WARNING, "leaked override search path");
+ overrideStack = list_delete_first(overrideStack);
+ list_free(entry->searchPath);
+ pfree(entry);
+ }
+
+ /* Activate the next level down. */
+ if (overrideStack)
+ {
+ entry = (OverrideStackEntry *) linitial(overrideStack);
+ activeSearchPath = entry->searchPath;
+ activeCreationNamespace = entry->creationNamespace;
+ }
+ else
+ {
+ /* If not baseSearchPathValid, this is useless but harmless */
+ activeSearchPath = baseSearchPath;
+ activeCreationNamespace = baseCreationNamespace;
+ }
}
/*
* initialization.
*/
if (doit)
- namespaceSearchPathValid = false;
+ baseSearchPathValid = false;
return newval;
}
MemoryContext oldcxt;
oldcxt = MemoryContextSwitchTo(TopMemoryContext);
- namespaceSearchPath = list_make1_oid(PG_CATALOG_NAMESPACE);
+ baseSearchPath = list_make1_oid(PG_CATALOG_NAMESPACE);
MemoryContextSwitchTo(oldcxt);
- defaultCreationNamespace = PG_CATALOG_NAMESPACE;
- firstExplicitNamespace = PG_CATALOG_NAMESPACE;
- namespaceSearchPathValid = true;
+ baseCreationNamespace = PG_CATALOG_NAMESPACE;
+ baseSearchPathValid = true;
namespaceUser = GetUserId();
+ activeSearchPath = baseSearchPath;
+ activeCreationNamespace = baseCreationNamespace;
}
else
{
NamespaceCallback,
(Datum) 0);
/* Force search path to be recomputed on next use */
- namespaceSearchPathValid = false;
+ baseSearchPathValid = false;
}
}
NamespaceCallback(Datum arg, Oid relid)
{
/* Force search path to be recomputed on next use */
- namespaceSearchPathValid = false;
+ baseSearchPathValid = false;
}
/*
recomputeNamespacePath();
- result = list_copy(namespaceSearchPath);
+ result = list_copy(activeSearchPath);
if (!includeImplicit)
{
- while (result && linitial_oid(result) != firstExplicitNamespace)
+ while (result && linitial_oid(result) != activeCreationNamespace)
result = list_delete_first(result);
}