diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 707ba7e28373dace9ee7428542e391b0697aabe3..0013c3f62731b86c6ddbbe201927ea734844118c 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -198,7 +198,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio * Toast tables for regular relations go in pg_toast; those for temp * relations go into the per-backend temp-toast-table namespace. */ - if (RelationUsesTempNamespace(rel)) + if (isTempOrToastNamespace(rel->rd_rel->relnamespace)) namespaceid = GetTempToastNamespace(); else namespaceid = PG_TOAST_NAMESPACE; diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 98bcb2fcf3370c72b0f0a7c0df76ebe4512e9ab0..cd6b13c09f32d2ba6de59cd174fe9cfd67e95d4c 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -800,7 +800,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString) Assert(rel); /* check read-only transaction */ - if (XactReadOnly && rel->rd_backend != MyBackendId) + if (XactReadOnly && !rel->rd_islocaltemp) PreventCommandIfReadOnly("COPY FROM"); cstate = BeginCopyFrom(rel, stmt->filename, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fbb6489915ec4068e77805829f4058b1dd6f82bf..c6cf44a8a8dd980dc7211f49d1cc5252542e4ba6 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -552,7 +552,7 @@ nextval_internal(Oid relid) RelationGetRelationName(seqrel)))); /* read-only transactions may only modify temp sequences */ - if (seqrel->rd_backend != MyBackendId) + if (!seqrel->rd_islocaltemp) PreventCommandIfReadOnly("nextval()"); if (elm->last != elm->cached) /* some numbers were cached */ @@ -845,7 +845,7 @@ do_setval(Oid relid, int64 next, bool iscalled) RelationGetRelationName(seqrel)))); /* read-only transactions may only modify temp sequences */ - if (seqrel->rd_backend != MyBackendId) + if (!seqrel->rd_islocaltemp) PreventCommandIfReadOnly("setval()"); /* lock page' buffer and read tuple */ diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index ab5ab940ade56f8347c7a64e194741e0be7ed04d..5d64461cdaa960c9a2caa6524dbe1176bddde61d 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1450,13 +1450,20 @@ MergeAttributes(List *schema, List *supers, char relpersistence, errmsg("inherited relation \"%s\" is not a table", parent->relname))); /* Permanent rels cannot inherit from temporary ones */ - if (relpersistence != RELPERSISTENCE_TEMP - && RelationUsesTempNamespace(relation)) + if (relpersistence != RELPERSISTENCE_TEMP && + relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot inherit from temporary relation \"%s\"", parent->relname))); + /* If existing rel is temp, it must belong to this session */ + if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP && + !relation->rd_islocaltemp) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot inherit from temporary relation of another session"))); + /* * We should have an UNDER permission flag for this, but for now, * demand that creator of a child table own the parent. @@ -5767,6 +5774,10 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("constraints on temporary tables may reference only temporary tables"))); + if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("constraints on temporary tables must involve temporary tables of this session"))); break; } @@ -8905,13 +8916,27 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode) ATSimplePermissions(parent_rel, ATT_TABLE); /* Permanent rels cannot inherit from temporary ones */ - if (RelationUsesTempNamespace(parent_rel) - && !RelationUsesTempNamespace(child_rel)) + if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && + child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot inherit from temporary relation \"%s\"", RelationGetRelationName(parent_rel)))); + /* If parent rel is temp, it must belong to this session */ + if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && + !parent_rel->rd_islocaltemp) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot inherit from temporary relation of another session"))); + + /* Ditto for the child */ + if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && + !child_rel->rd_islocaltemp) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot inherit to temporary relation of another session"))); + /* * Check for duplicates in the list of parents, and determine the highest * inhseqno already present; we'll use the next one for the new parent. diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c index 2ccdc0cee6e598ac6871a9a094353c32c3817044..03975fcea446b73ccc3b9a56cb645ca13ce96778 100644 --- a/src/backend/utils/adt/dbsize.c +++ b/src/backend/utils/adt/dbsize.c @@ -259,6 +259,9 @@ pg_tablespace_size_name(PG_FUNCTION_ARGS) /* * calculate size of (one fork of) a relation + * + * Note: we can safely apply this to temp tables of other sessions, so there + * is no check here or at the call sites for that. */ static int64 calculate_relation_size(RelFileNode *rfn, BackendId backend, ForkNumber forknum) @@ -313,7 +316,7 @@ pg_relation_size(PG_FUNCTION_ARGS) * that makes queries like "SELECT pg_relation_size(oid) FROM pg_class" * less robust, because while we scan pg_class with an MVCC snapshot, * someone else might drop the table. It's better to return NULL for - * alread-dropped tables than throw an error and abort the whole query. + * already-dropped tables than throw an error and abort the whole query. */ if (rel == NULL) PG_RETURN_NULL(); diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index a64d739cebc87a462faa9d9851b35a4e8f2fb09b..bd7f567f1e93cad7be3f6ee36e3c3f50f8e4e76e 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -852,20 +852,33 @@ RelationBuildDesc(Oid targetRelId, bool insertIt) case RELPERSISTENCE_UNLOGGED: case RELPERSISTENCE_PERMANENT: relation->rd_backend = InvalidBackendId; + relation->rd_islocaltemp = false; break; case RELPERSISTENCE_TEMP: if (isTempOrToastNamespace(relation->rd_rel->relnamespace)) + { relation->rd_backend = MyBackendId; + relation->rd_islocaltemp = true; + } else { /* - * If it's a local temp table, but not one of ours, we have to - * use the slow, grotty method to figure out the owning - * backend. + * If it's a temp table, but not one of ours, we have to use + * the slow, grotty method to figure out the owning backend. + * + * Note: it's possible that rd_backend gets set to MyBackendId + * here, in case we are looking at a pg_class entry left over + * from a crashed backend that coincidentally had the same + * BackendId we're using. We should *not* consider such a + * table to be "ours"; this is why we need the separate + * rd_islocaltemp flag. The pg_class entry will get flushed + * if/when we clean out the corresponding temp table namespace + * in preparation for using it. */ relation->rd_backend = GetTempNamespaceBackendId(relation->rd_rel->relnamespace); Assert(relation->rd_backend != InvalidBackendId); + relation->rd_islocaltemp = false; } break; default: @@ -1386,6 +1399,7 @@ formrdesc(const char *relationName, Oid relationReltype, relation->rd_createSubid = InvalidSubTransactionId; relation->rd_newRelfilenodeSubid = InvalidSubTransactionId; relation->rd_backend = InvalidBackendId; + relation->rd_islocaltemp = false; /* * initialize relation tuple form @@ -2535,16 +2549,19 @@ RelationBuildLocalRelation(const char *relname, /* needed when bootstrapping: */ rel->rd_rel->relowner = BOOTSTRAP_SUPERUSERID; - /* set up persistence; rd_backend is a function of persistence type */ + /* set up persistence and relcache fields dependent on it */ rel->rd_rel->relpersistence = relpersistence; switch (relpersistence) { case RELPERSISTENCE_UNLOGGED: case RELPERSISTENCE_PERMANENT: rel->rd_backend = InvalidBackendId; + rel->rd_islocaltemp = false; break; case RELPERSISTENCE_TEMP: + Assert(isTempOrToastNamespace(relnamespace)); rel->rd_backend = MyBackendId; + rel->rd_islocaltemp = true; break; default: elog(ERROR, "invalid relpersistence: %c", relpersistence); diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 4669d8a67ef4298c591accbc81710c76e1efa535..3bf90d28f90c5ce1f3f948bcdaab24930cad8879 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -85,6 +85,7 @@ typedef struct RelationData bool rd_isvalid; /* relcache entry is valid */ char rd_indexvalid; /* state of rd_indexlist: 0 = not valid, 1 = * valid, 2 = temporarily forced */ + bool rd_islocaltemp; /* rel is a temp rel of this session */ /* * rd_createSubid is the ID of the highest subtransaction the rel has @@ -362,22 +363,16 @@ typedef struct StdRdOptions #define RelationUsesLocalBuffers(relation) \ ((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP) -/* - * RelationUsesTempNamespace - * True if relation's catalog entries live in a private namespace. - */ -#define RelationUsesTempNamespace(relation) \ - ((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP) - /* * RELATION_IS_LOCAL * If a rel is either temp or newly created in the current transaction, - * it can be assumed to be visible only to the current backend. + * it can be assumed to be accessible only to the current backend. + * This is typically used to decide that we can skip acquiring locks. * * Beware of multiple eval of argument */ #define RELATION_IS_LOCAL(relation) \ - ((relation)->rd_backend == MyBackendId || \ + ((relation)->rd_islocaltemp || \ (relation)->rd_createSubid != InvalidSubTransactionId) /* @@ -387,8 +382,8 @@ typedef struct StdRdOptions * Beware of multiple eval of argument */ #define RELATION_IS_OTHER_TEMP(relation) \ - ((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP \ - && (relation)->rd_backend != MyBackendId) + ((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP && \ + !(relation)->rd_islocaltemp) /* routines in utils/cache/relcache.c */ extern void RelationIncrementReferenceCount(Relation rel);