* and implementing search-path-controlled searches.
*
*
- * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
*/
#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/objectaccess.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_conversion.h"
#include "storage/sinval.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/catcache.h"
#include "utils/guc.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
* 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. 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
+ * *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.)
* 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
+ * 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.)
* 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
+ * 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).
/*
* 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.
+ * 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.
+ * 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,
+ * 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
* Given a RangeVar describing an existing relation,
* select the proper namespace and look up the relation OID.
*
- * If the relation is not found, return InvalidOid if missing_ok = true,
- * otherwise raise an error.
+ * If the schema or relation is not found, return InvalidOid if missing_ok
+ * = true, otherwise raise an error.
*
* If nowait = true, throw an error if we'd have to wait for a lock.
*
}
/*
- * DDL operations can change the results of a name lookup. Since all such
+ * 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)
* 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, no 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
+ * 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 (;;)
{
Oid namespaceId;
- namespaceId = LookupExplicitNamespace(relation->schemaname);
+ namespaceId = LookupExplicitNamespace(relation->schemaname, missing_ok);
+
+ /*
+ * For missing_ok, allow a non-existant schema name to
+ * return InvalidOid.
+ */
if (namespaceId != myTempNamespace)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
Oid namespaceId;
/* use exact schema given */
- namespaceId = LookupExplicitNamespace(relation->schemaname);
- relId = get_relname_relid(relation->relname, namespaceId);
+ namespaceId = LookupExplicitNamespace(relation->schemaname, missing_ok);
+ if (missing_ok && !OidIsValid(namespaceId))
+ relId = InvalidOid;
+ else
+ relId = get_relname_relid(relation->relname, namespaceId);
}
else
{
break;
/*
- * Something may have changed. Let's repeat the name lookup, to make
+ * Something may have changed. Let's repeat the name lookup, to make
* sure this name still references the same relation it did
* previously.
*/
* 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 acquire on the existing
+ * 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
switch (newRelation->relpersistence)
{
case RELPERSISTENCE_TEMP:
- if (!isTempOrToastNamespace(nspid))
+ if (!isTempOrTempToastNamespace(nspid))
{
if (isAnyTempNamespace(nspid))
ereport(ERROR,
}
break;
case RELPERSISTENCE_PERMANENT:
- if (isTempOrToastNamespace(nspid))
+ if (isTempOrTempToastNamespace(nspid))
newRelation->relpersistence = RELPERSISTENCE_TEMP;
else if (isAnyTempNamespace(nspid))
ereport(ERROR,
* 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
+ * 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).
* 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 expand_variadic, bool expand_defaults,
+ bool missing_ok)
{
FuncCandidateList resultList = NULL;
bool any_special = false;
if (schemaname)
{
/* use exact schema given */
- namespaceId = LookupExplicitNamespace(schemaname);
+ namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
+ if (!OidIsValid(namespaceId))
+ return NULL;
}
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
+ * so. If expand_variadic is false, we should just ignore
* variadic-ness.
*/
if (pronargs <= nargs && expand_variadic)
*/
effective_nargs = Max(pronargs, nargs);
newResult = (FuncCandidateList)
- palloc(sizeof(struct _FuncCandidateList) - sizeof(Oid)
- + effective_nargs * sizeof(Oid));
+ palloc(offsetof(struct _FuncCandidateList, args) +
+ effective_nargs * sizeof(Oid));
newResult->pathpos = pathpos;
newResult->oid = HeapTupleGetOid(proctup);
newResult->nargs = effective_nargs;
if (prevResult)
{
/*
- * We have a match with a previous result. Decide which one
+ * 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
visible = false;
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
- nargs, NIL, false, false);
+ nargs, NIL, false, false, false);
for (; clist; clist = clist->next)
{
* a postfix op.
*
* If the operator name is not schema-qualified, it is sought in the current
- * namespace search path.
+ * 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)
{
/* search only in exact schema given */
Oid namespaceId;
- HeapTuple opertup;
- namespaceId = LookupExplicitNamespace(schemaname);
- opertup = SearchSysCache4(OPERNAMENSP,
- CStringGetDatum(opername),
- ObjectIdGetDatum(oprleft),
- ObjectIdGetDatum(oprright),
- ObjectIdGetDatum(namespaceId));
- if (HeapTupleIsValid(opertup))
+ namespaceId = LookupExplicitNamespace(schemaname, true);
+ if (OidIsValid(namespaceId))
{
- Oid result = HeapTupleGetOid(opertup);
+ HeapTuple opertup;
- ReleaseSysCache(opertup);
- return result;
+ opertup = SearchSysCache4(OPERNAMENSP,
+ CStringGetDatum(opername),
+ ObjectIdGetDatum(oprleft),
+ ObjectIdGetDatum(oprright),
+ ObjectIdGetDatum(namespaceId));
+ if (HeapTupleIsValid(opertup))
+ {
+ Oid result = HeapTupleGetOid(opertup);
+
+ ReleaseSysCache(opertup);
+ return result;
+ }
}
+
return InvalidOid;
}
* identical entries in later namespaces.
*
* The returned items always have two args[] entries --- one or the other
- * will be InvalidOid for a prefix or postfix oprkind. nargs is 2, too.
+ * will be InvalidOid for a prefix or postfix oprkind. nargs is 2, too.
*/
FuncCandidateList
-OpernameGetCandidates(List *names, char oprkind)
+OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok)
{
FuncCandidateList resultList = NULL;
char *resultSpace = NULL;
if (schemaname)
{
/* use exact schema given */
- namespaceId = LookupExplicitNamespace(schemaname);
+ namespaceId = LookupExplicitNamespace(schemaname, missing_schema_ok);
+ if (missing_schema_ok && !OidIsValid(namespaceId))
+ return NULL;
}
else
{
* separate palloc for each operator, but profiling revealed that the
* pallocs used an unreasonably large fraction of parsing time.
*/
-#define SPACE_PER_OP MAXALIGN(sizeof(struct _FuncCandidateList) + sizeof(Oid))
+#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);
* 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.
+ * operator that would be found by OpernameGetOprid.
*/
char *oprname = NameStr(oprform->oprname);
if (schemaname)
{
/* use exact schema given */
- namespaceId = LookupExplicitNamespace(schemaname);
- prsoid = GetSysCacheOid2(TSPARSERNAMENSP,
- PointerGetDatum(parser_name),
- ObjectIdGetDatum(namespaceId));
+ namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
+ if (missing_ok && !OidIsValid(namespaceId))
+ prsoid = InvalidOid;
+ else
+ prsoid = GetSysCacheOid2(TSPARSERNAMENSP,
+ PointerGetDatum(parser_name),
+ ObjectIdGetDatum(namespaceId));
}
else
{
if (schemaname)
{
/* use exact schema given */
- namespaceId = LookupExplicitNamespace(schemaname);
- dictoid = GetSysCacheOid2(TSDICTNAMENSP,
- PointerGetDatum(dict_name),
- ObjectIdGetDatum(namespaceId));
+ namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
+ if (missing_ok && !OidIsValid(namespaceId))
+ dictoid = InvalidOid;
+ else
+ dictoid = GetSysCacheOid2(TSDICTNAMENSP,
+ PointerGetDatum(dict_name),
+ ObjectIdGetDatum(namespaceId));
}
else
{
if (schemaname)
{
/* use exact schema given */
- namespaceId = LookupExplicitNamespace(schemaname);
- tmploid = GetSysCacheOid2(TSTEMPLATENAMENSP,
- PointerGetDatum(template_name),
- ObjectIdGetDatum(namespaceId));
+ namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
+ if (missing_ok && !OidIsValid(namespaceId))
+ tmploid = InvalidOid;
+ else
+ tmploid = GetSysCacheOid2(TSTEMPLATENAMENSP,
+ PointerGetDatum(template_name),
+ ObjectIdGetDatum(namespaceId));
}
else
{
if (schemaname)
{
/* use exact schema given */
- namespaceId = LookupExplicitNamespace(schemaname);
- cfgoid = GetSysCacheOid2(TSCONFIGNAMENSP,
- PointerGetDatum(config_name),
- ObjectIdGetDatum(namespaceId));
+ namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
+ if (missing_ok && !OidIsValid(namespaceId))
+ cfgoid = InvalidOid;
+ else
+ cfgoid = GetSysCacheOid2(TSCONFIGNAMENSP,
+ PointerGetDatum(config_name),
+ ObjectIdGetDatum(namespaceId));
}
else
{
/*
* TSConfigIsVisible
* Determine whether a text search configuration (identified by OID)
- * is visible in the current search path. Visible means "would be found
+ * is visible in the current search path. Visible means "would be found
* by searching for the unqualified text search configuration name".
*/
bool
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
* Process an explicitly-specified schema name: look up the schema
* and verify we have USAGE (lookup) rights in it.
*
- * Returns the namespace OID. Raises ereport if any problem.
+ * Returns the namespace OID
*/
Oid
-LookupExplicitNamespace(const char *nspname)
+LookupExplicitNamespace(const char *nspname, bool missing_ok)
{
Oid namespaceId;
AclResult aclresult;
/*
* 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.
+ * so might create problems for some callers --- just fall through.
*/
}
- namespaceId = get_namespace_oid(nspname, false);
+ namespaceId = get_namespace_oid(nspname, missing_ok);
+ if (missing_ok && !OidIsValid(namespaceId))
+ return InvalidOid;
aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
nspname);
+ /* Schema search hook for this lookup */
+ InvokeNamespaceSearchHook(namespaceId, true);
return namespaceId;
}
/*
* Common checks on switching namespaces.
*
- * We complain if (1) the old and new namespaces are the same, (2) either the
- * old or new namespaces is a temporary schema (or temporary toast schema), or
- * (3) either the old or new namespaces is the TOAST schema.
+ * 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, Oid classid, Oid objid)
+CheckSetNamespace(Oid oldNspOid, Oid nspOid)
{
- if (oldNspOid == nspOid)
- ereport(ERROR,
- (classid == RelationRelationId ?
- errcode(ERRCODE_DUPLICATE_TABLE) :
- classid == ProcedureRelationId ?
- errcode(ERRCODE_DUPLICATE_FUNCTION) :
- errcode(ERRCODE_DUPLICATE_OBJECT),
- errmsg("%s is already in schema \"%s\"",
- getObjectDescriptionOids(classid, objid),
- get_namespace_name(nspOid))));
-
/* disallow renaming into or out of temp schemas */
if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid))
ereport(ERROR,
/*
* 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
+ * If missing_ok is false, throw an error if namespace name not found. If
* true, just return InvalidOid.
*/
Oid
if (IsA(name, String))
appendStringInfoString(&string, strVal(name));
else if (IsA(name, A_Star))
- appendStringInfoString(&string, "*");
+ appendStringInfoChar(&string, '*');
else
elog(ERROR, "unexpected node type in name list: %d",
(int) nodeTag(name));
}
/*
- * isTempOrToastNamespace - is the given namespace my temporary-table
+ * isTempOrTempToastNamespace - is the given namespace my temporary-table
* namespace or my temporary-toast-table namespace?
*/
bool
-isTempOrToastNamespace(Oid namespaceId)
+isTempOrTempToastNamespace(Oid namespaceId)
{
if (OidIsValid(myTempNamespace) &&
(myTempNamespace == namespaceId || myTempToastNamespace == namespaceId))
isOtherTempNamespace(Oid namespaceId)
{
/* If it's my own temp namespace, say "false" */
- if (isTempOrToastNamespace(namespaceId))
+ if (isTempOrTempToastNamespace(namespaceId))
return false;
/* Else, if it's any temp namespace, say "true" */
return isAnyTempNamespace(namespaceId);
/*
* GetTempToastNamespace - get the OID of my temporary-toast-table namespace,
- * which must already be assigned. (This is only used when creating a toast
+ * 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
return result;
}
+/*
+ * OverrideSearchPathMatchesCurrent - does path match current setting?
+ */
+bool
+OverrideSearchPathMatchesCurrent(OverrideSearchPath *path)
+{
+ ListCell *lc,
+ *lcp;
+
+ recomputeNamespacePath();
+
+ /* 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(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(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(lc);
+ else
+ return false;
+ }
+ if (lc)
+ return false;
+ return true;
+}
+
/*
* PushOverrideSearchPath - temporarily override the search path
*
*
* It's possible that newpath->useTemp is set but there is no longer any
* active temp namespace, if the path was saved during a transaction that
- * created a temp namespace and was later rolled back. In that case we just
- * ignore useTemp. A plausible alternative would be to create a new temp
+ * created a temp namespace and was later rolled back. In that case we just
+ * ignore useTemp. A plausible alternative would be to create a new temp
* namespace, but for existing callers that's not necessary because an empty
* temp namespace wouldn't affect their results anyway.
*
firstNS = linitial_oid(oidlist);
/*
- * Add any implicitly-searched namespaces to the list. Note these go on
+ * 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 (schemaname)
{
/* use exact schema given */
- namespaceId = LookupExplicitNamespace(schemaname);
+ namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
+ if (missing_ok && !OidIsValid(namespaceId))
+ return InvalidOid;
/* first try for encoding-specific entry, then any-encoding */
colloid = GetSysCacheOid3(COLLNAMEENCNSP,
if (schemaname)
{
/* use exact schema given */
- namespaceId = LookupExplicitNamespace(schemaname);
- conoid = GetSysCacheOid2(CONNAMENSP,
- PointerGetDatum(conversion_name),
- ObjectIdGetDatum(namespaceId));
+ namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
+ if (missing_ok && !OidIsValid(namespaceId))
+ conoid = InvalidOid;
+ else
+ conoid = GetSysCacheOid2(CONNAMENSP,
+ PointerGetDatum(conversion_name),
+ ObjectIdGetDatum(namespaceId));
}
else
{
if (OidIsValid(namespaceId) &&
!list_member_oid(oidlist, namespaceId) &&
pg_namespace_aclcheck(namespaceId, roleid,
- ACL_USAGE) == ACLCHECK_OK)
+ ACL_USAGE) == ACLCHECK_OK &&
+ InvokeNamespaceSearchHook(namespaceId, false))
oidlist = lappend_oid(oidlist, namespaceId);
}
}
/* pg_temp --- substitute temp namespace, if any */
if (OidIsValid(myTempNamespace))
{
- if (!list_member_oid(oidlist, myTempNamespace))
+ if (!list_member_oid(oidlist, myTempNamespace) &&
+ InvokeNamespaceSearchHook(myTempNamespace, false))
oidlist = lappend_oid(oidlist, myTempNamespace);
}
else
if (OidIsValid(namespaceId) &&
!list_member_oid(oidlist, namespaceId) &&
pg_namespace_aclcheck(namespaceId, roleid,
- ACL_USAGE) == ACLCHECK_OK)
+ ACL_USAGE) == ACLCHECK_OK &&
+ InvokeNamespaceSearchHook(namespaceId, false))
oidlist = lappend_oid(oidlist, namespaceId);
}
}
/*
- * Remember the first member of the explicit list. (Note: this is
+ * 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.)
*/
firstNS = linitial_oid(oidlist);
/*
- * Add any implicitly-searched namespaces to the list. Note these go on
+ * 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.
*/
/*
* First, do permission check to see if we are authorized to make temp
- * tables. We use a nonstandard error message here since "databasename:
+ * 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;
* Do not allow a Hot Standby slave 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 BackendId N on the
- * master, not to a slave session with the same BackendId. We should not
+ * master, not to a slave session with the same BackendId. 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
+ * 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.
*/
(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 in parallel mode")));
+
snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId);
namespaceId = get_namespace_oid(namespaceName, true);
* End-of-transaction cleanup for namespaces.
*/
void
-AtEOXact_Namespace(bool isCommit)
+AtEOXact_Namespace(bool isCommit, bool parallel)
{
/*
* If we abort the transaction in which a temp namespace was selected,
* at backend shutdown. (We only want to register the callback once per
* session, so this is a good place to do it.)
*/
- if (myTempNamespaceSubID != InvalidSubTransactionId)
+ if (myTempNamespaceSubID != InvalidSubTransactionId && !parallel)
{
if (isCommit)
- on_shmem_exit(RemoveTempRelationsCallback, 0);
+ before_shmem_exit(RemoveTempRelationsCallback, 0);
else
{
myTempNamespace = InvalidOid;
/*
* 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
+ * 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.
*/
/*
* 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,
+ * 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,
* 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
- * operate on SnapshotNow semantics and so might see the object as already
- * gone when it's still visible to the MVCC snapshot. (There is no race
+ * 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. (There is no race
* condition in the current coding because we don't accept sinval messages
* between the SearchSysCacheExists test and the subsequent lookup.)
*/