Skip to content

Commit 6919b7e

Browse files
committed
Fix failure to ignore leftover temp tables after a server crash.
During crash recovery, we remove disk files belonging to temporary tables, but the system catalog entries for such tables are intentionally not cleaned up right away. Instead, the first backend that uses a temp schema is expected to clean out any leftover objects therein. This approach requires that we be careful to ignore leftover temp tables (since any actual access attempt would fail), *even if their BackendId matches our session*, if we have not yet established use of the session's corresponding temp schema. That worked fine in the past, but was broken by commit debcec7 which incorrectly removed the rd_islocaltemp relcache flag. Put it back, and undo various changes that substituted tests like "rel->rd_backend == MyBackendId" for use of a state-aware flag. Per trouble report from Heikki Linnakangas. Back-patch to 9.1 where the erroneous change was made. In the back branches, be careful to add rd_islocaltemp in a spot in the struct that was alignment padding before, so as not to break existing add-on code.
1 parent c299477 commit 6919b7e

File tree

7 files changed

+64
-24
lines changed

7 files changed

+64
-24
lines changed

src/backend/catalog/toasting.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
196196
* Toast tables for regular relations go in pg_toast; those for temp
197197
* relations go into the per-backend temp-toast-table namespace.
198198
*/
199-
if (RelationUsesTempNamespace(rel))
199+
if (isTempOrToastNamespace(rel->rd_rel->relnamespace))
200200
namespaceid = GetTempToastNamespace();
201201
else
202202
namespaceid = PG_TOAST_NAMESPACE;

src/backend/commands/copy.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
806806
Assert(rel);
807807

808808
/* check read-only transaction */
809-
if (XactReadOnly && rel->rd_backend != MyBackendId)
809+
if (XactReadOnly && !rel->rd_islocaltemp)
810810
PreventCommandIfReadOnly("COPY FROM");
811811

812812
cstate = BeginCopyFrom(rel, stmt->filename,

src/backend/commands/sequence.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ nextval_internal(Oid relid)
553553
RelationGetRelationName(seqrel))));
554554

555555
/* read-only transactions may only modify temp sequences */
556-
if (seqrel->rd_backend != MyBackendId)
556+
if (!seqrel->rd_islocaltemp)
557557
PreventCommandIfReadOnly("nextval()");
558558

559559
if (elm->last != elm->cached) /* some numbers were cached */
@@ -846,7 +846,7 @@ do_setval(Oid relid, int64 next, bool iscalled)
846846
RelationGetRelationName(seqrel))));
847847

848848
/* read-only transactions may only modify temp sequences */
849-
if (seqrel->rd_backend != MyBackendId)
849+
if (!seqrel->rd_islocaltemp)
850850
PreventCommandIfReadOnly("setval()");
851851

852852
/* lock page' buffer and read tuple */

src/backend/commands/tablecmds.c

+29-4
Original file line numberDiff line numberDiff line change
@@ -1451,13 +1451,20 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
14511451
errmsg("inherited relation \"%s\" is not a table",
14521452
parent->relname)));
14531453
/* Permanent rels cannot inherit from temporary ones */
1454-
if (relpersistence != RELPERSISTENCE_TEMP
1455-
&& RelationUsesTempNamespace(relation))
1454+
if (relpersistence != RELPERSISTENCE_TEMP &&
1455+
relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
14561456
ereport(ERROR,
14571457
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
14581458
errmsg("cannot inherit from temporary relation \"%s\"",
14591459
parent->relname)));
14601460

1461+
/* If existing rel is temp, it must belong to this session */
1462+
if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
1463+
!relation->rd_islocaltemp)
1464+
ereport(ERROR,
1465+
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
1466+
errmsg("cannot inherit from temporary relation of another session")));
1467+
14611468
/*
14621469
* We should have an UNDER permission flag for this, but for now,
14631470
* demand that creator of a child table own the parent.
@@ -5768,6 +5775,10 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
57685775
ereport(ERROR,
57695776
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
57705777
errmsg("constraints on temporary tables may reference only temporary tables")));
5778+
if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
5779+
ereport(ERROR,
5780+
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
5781+
errmsg("constraints on temporary tables must involve temporary tables of this session")));
57715782
break;
57725783
}
57735784

@@ -8884,13 +8895,27 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
88848895
ATSimplePermissions(parent_rel, ATT_TABLE);
88858896

88868897
/* Permanent rels cannot inherit from temporary ones */
8887-
if (RelationUsesTempNamespace(parent_rel)
8888-
&& !RelationUsesTempNamespace(child_rel))
8898+
if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
8899+
child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
88898900
ereport(ERROR,
88908901
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
88918902
errmsg("cannot inherit from temporary relation \"%s\"",
88928903
RelationGetRelationName(parent_rel))));
88938904

8905+
/* If parent rel is temp, it must belong to this session */
8906+
if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
8907+
!parent_rel->rd_islocaltemp)
8908+
ereport(ERROR,
8909+
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
8910+
errmsg("cannot inherit from temporary relation of another session")));
8911+
8912+
/* Ditto for the child */
8913+
if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
8914+
!child_rel->rd_islocaltemp)
8915+
ereport(ERROR,
8916+
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
8917+
errmsg("cannot inherit to temporary relation of another session")));
8918+
88948919
/*
88958920
* Check for duplicates in the list of parents, and determine the highest
88968921
* inhseqno already present; we'll use the next one for the new parent.

src/backend/utils/adt/dbsize.c

+4-1
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,9 @@ pg_tablespace_size_name(PG_FUNCTION_ARGS)
260260

261261
/*
262262
* calculate size of (one fork of) a relation
263+
*
264+
* Note: we can safely apply this to temp tables of other sessions, so there
265+
* is no check here or at the call sites for that.
263266
*/
264267
static int64
265268
calculate_relation_size(RelFileNode *rfn, BackendId backend, ForkNumber forknum)
@@ -314,7 +317,7 @@ pg_relation_size(PG_FUNCTION_ARGS)
314317
* that makes queries like "SELECT pg_relation_size(oid) FROM pg_class"
315318
* less robust, because while we scan pg_class with an MVCC snapshot,
316319
* someone else might drop the table. It's better to return NULL for
317-
* alread-dropped tables than throw an error and abort the whole query.
320+
* already-dropped tables than throw an error and abort the whole query.
318321
*/
319322
if (rel == NULL)
320323
PG_RETURN_NULL();

src/backend/utils/cache/relcache.c

+21-4
Original file line numberDiff line numberDiff line change
@@ -853,20 +853,33 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
853853
case RELPERSISTENCE_UNLOGGED:
854854
case RELPERSISTENCE_PERMANENT:
855855
relation->rd_backend = InvalidBackendId;
856+
relation->rd_islocaltemp = false;
856857
break;
857858
case RELPERSISTENCE_TEMP:
858859
if (isTempOrToastNamespace(relation->rd_rel->relnamespace))
860+
{
859861
relation->rd_backend = MyBackendId;
862+
relation->rd_islocaltemp = true;
863+
}
860864
else
861865
{
862866
/*
863-
* If it's a local temp table, but not one of ours, we have to
864-
* use the slow, grotty method to figure out the owning
865-
* backend.
867+
* If it's a temp table, but not one of ours, we have to use
868+
* the slow, grotty method to figure out the owning backend.
869+
*
870+
* Note: it's possible that rd_backend gets set to MyBackendId
871+
* here, in case we are looking at a pg_class entry left over
872+
* from a crashed backend that coincidentally had the same
873+
* BackendId we're using. We should *not* consider such a
874+
* table to be "ours"; this is why we need the separate
875+
* rd_islocaltemp flag. The pg_class entry will get flushed
876+
* if/when we clean out the corresponding temp table namespace
877+
* in preparation for using it.
866878
*/
867879
relation->rd_backend =
868880
GetTempNamespaceBackendId(relation->rd_rel->relnamespace);
869881
Assert(relation->rd_backend != InvalidBackendId);
882+
relation->rd_islocaltemp = false;
870883
}
871884
break;
872885
default:
@@ -1387,6 +1400,7 @@ formrdesc(const char *relationName, Oid relationReltype,
13871400
relation->rd_createSubid = InvalidSubTransactionId;
13881401
relation->rd_newRelfilenodeSubid = InvalidSubTransactionId;
13891402
relation->rd_backend = InvalidBackendId;
1403+
relation->rd_islocaltemp = false;
13901404

13911405
/*
13921406
* initialize relation tuple form
@@ -2538,16 +2552,19 @@ RelationBuildLocalRelation(const char *relname,
25382552
/* needed when bootstrapping: */
25392553
rel->rd_rel->relowner = BOOTSTRAP_SUPERUSERID;
25402554

2541-
/* set up persistence; rd_backend is a function of persistence type */
2555+
/* set up persistence and relcache fields dependent on it */
25422556
rel->rd_rel->relpersistence = relpersistence;
25432557
switch (relpersistence)
25442558
{
25452559
case RELPERSISTENCE_UNLOGGED:
25462560
case RELPERSISTENCE_PERMANENT:
25472561
rel->rd_backend = InvalidBackendId;
2562+
rel->rd_islocaltemp = false;
25482563
break;
25492564
case RELPERSISTENCE_TEMP:
2565+
Assert(isTempOrToastNamespace(relnamespace));
25502566
rel->rd_backend = MyBackendId;
2567+
rel->rd_islocaltemp = true;
25512568
break;
25522569
default:
25532570
elog(ERROR, "invalid relpersistence: %c", relpersistence);

src/include/utils/rel.h

+6-11
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ typedef struct RelationData
8181
struct SMgrRelationData *rd_smgr; /* cached file handle, or NULL */
8282
int rd_refcnt; /* reference count */
8383
BackendId rd_backend; /* owning backend id, if temporary relation */
84+
bool rd_islocaltemp; /* rel is a temp rel of this session */
8485
bool rd_isnailed; /* rel is nailed in cache */
8586
bool rd_isvalid; /* relcache entry is valid */
8687
char rd_indexvalid; /* state of rd_indexlist: 0 = not valid, 1 =
@@ -362,22 +363,16 @@ typedef struct StdRdOptions
362363
#define RelationUsesLocalBuffers(relation) \
363364
((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
364365

365-
/*
366-
* RelationUsesTempNamespace
367-
* True if relation's catalog entries live in a private namespace.
368-
*/
369-
#define RelationUsesTempNamespace(relation) \
370-
((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
371-
372366
/*
373367
* RELATION_IS_LOCAL
374368
* If a rel is either temp or newly created in the current transaction,
375-
* it can be assumed to be visible only to the current backend.
369+
* it can be assumed to be accessible only to the current backend.
370+
* This is typically used to decide that we can skip acquiring locks.
376371
*
377372
* Beware of multiple eval of argument
378373
*/
379374
#define RELATION_IS_LOCAL(relation) \
380-
((relation)->rd_backend == MyBackendId || \
375+
((relation)->rd_islocaltemp || \
381376
(relation)->rd_createSubid != InvalidSubTransactionId)
382377

383378
/*
@@ -387,8 +382,8 @@ typedef struct StdRdOptions
387382
* Beware of multiple eval of argument
388383
*/
389384
#define RELATION_IS_OTHER_TEMP(relation) \
390-
((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP \
391-
&& (relation)->rd_backend != MyBackendId)
385+
((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP && \
386+
!(relation)->rd_islocaltemp)
392387

393388
/* routines in utils/cache/relcache.c */
394389
extern void RelationIncrementReferenceCount(Relation rel);

0 commit comments

Comments
 (0)