Skip to content

Commit 2dad0f4

Browse files
committed
Reject modifying a temp table of another session with ALTER TABLE.
Normally this case isn't even reachable by non-superusers, since permissions checks prevent naming such a table. However, it is possible to make it happen by altering a parent table whose child is another session's temp table. We definitely can't support any such ALTER that requires modifying the contents of such a table, since we lack access to the other session's temporary-buffer pool. But there seems no good reason to allow it even if it'd only require changing catalog contents. One reason not to allow it is that we'd rather not expose the implementation-dependent behavior of whether a specific ALTER requires touching the table contents. Another is that there may be (in future, even if not today) optimizations that assume that a session's own temp tables won't be modified by other sessions. Hence, add a RELATION_IS_OTHER_TEMP() check to all the places where ALTER TABLE currently does CheckTableNotInUse(). (I looked through all other callers of CheckTableNotInUse(), and they seem OK already.) Per bug #18492 from Alexander Lakhin. Back-patch to all supported branches. Discussion: https://postgr.es/m/18492-c7a2634bf4968763@postgresql.org
1 parent 0f7d133 commit 2dad0f4

File tree

1 file changed

+47
-13
lines changed

1 file changed

+47
-13
lines changed

src/backend/commands/tablecmds.c

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
382382
static void validateForeignKeyConstraint(char *conname,
383383
Relation rel, Relation pkrel,
384384
Oid pkindOid, Oid constraintOid);
385+
static void CheckAlterTableIsSafe(Relation rel);
385386
static void ATController(AlterTableStmt *parsetree,
386387
Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
387388
AlterTableUtilityContext *context);
@@ -3982,6 +3983,37 @@ CheckTableNotInUse(Relation rel, const char *stmt)
39823983
stmt, RelationGetRelationName(rel))));
39833984
}
39843985

3986+
/*
3987+
* CheckAlterTableIsSafe
3988+
* Verify that it's safe to allow ALTER TABLE on this relation.
3989+
*
3990+
* This consists of CheckTableNotInUse() plus a check that the relation
3991+
* isn't another session's temp table. We must split out the temp-table
3992+
* check because there are callers of CheckTableNotInUse() that don't want
3993+
* that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
3994+
* an orphaned temp schema.) Compare truncate_check_activity().
3995+
*/
3996+
static void
3997+
CheckAlterTableIsSafe(Relation rel)
3998+
{
3999+
/*
4000+
* Don't allow ALTER on temp tables of other backends. Their local buffer
4001+
* manager is not going to cope if we need to change the table's contents.
4002+
* Even if we don't, there may be optimizations that assume temp tables
4003+
* aren't subject to such interference.
4004+
*/
4005+
if (RELATION_IS_OTHER_TEMP(rel))
4006+
ereport(ERROR,
4007+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4008+
errmsg("cannot alter temporary tables of other sessions")));
4009+
4010+
/*
4011+
* Also check for active uses of the relation in the current transaction,
4012+
* including open scans and pending AFTER trigger events.
4013+
*/
4014+
CheckTableNotInUse(rel, "ALTER TABLE");
4015+
}
4016+
39854017
/*
39864018
* AlterTableLookupRelation
39874019
* Look up, and lock, the OID for the relation named by an alter table
@@ -4056,7 +4088,7 @@ AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
40564088
/* Caller is required to provide an adequate lock. */
40574089
rel = relation_open(context->relid, NoLock);
40584090

4059-
CheckTableNotInUse(rel, "ALTER TABLE");
4091+
CheckAlterTableIsSafe(rel);
40604092

40614093
ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
40624094
}
@@ -5425,7 +5457,9 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
54255457

54265458
/*
54275459
* Don't allow rewrite on temp tables of other backends ... their
5428-
* local buffer manager is not going to cope.
5460+
* local buffer manager is not going to cope. (This is redundant
5461+
* with the check in CheckAlterTableIsSafe, but for safety we'll
5462+
* check here too.)
54295463
*/
54305464
if (RELATION_IS_OTHER_TEMP(OldHeap))
54315465
ereport(ERROR,
@@ -6169,7 +6203,7 @@ ATSimpleRecursion(List **wqueue, Relation rel,
61696203
continue;
61706204
/* find_all_inheritors already got lock */
61716205
childrel = relation_open(childrelid, NoLock);
6172-
CheckTableNotInUse(childrel, "ALTER TABLE");
6206+
CheckAlterTableIsSafe(childrel);
61736207
ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
61746208
relation_close(childrel, NoLock);
61756209
}
@@ -6178,7 +6212,7 @@ ATSimpleRecursion(List **wqueue, Relation rel,
61786212

61796213
/*
61806214
* Obtain list of partitions of the given table, locking them all at the given
6181-
* lockmode and ensuring that they all pass CheckTableNotInUse.
6215+
* lockmode and ensuring that they all pass CheckAlterTableIsSafe.
61826216
*
61836217
* This function is a no-op if the given relation is not a partitioned table;
61846218
* in particular, nothing is done if it's a legacy inheritance parent.
@@ -6199,7 +6233,7 @@ ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
61996233

62006234
/* find_all_inheritors already got lock */
62016235
childrel = table_open(lfirst_oid(cell), NoLock);
6202-
CheckTableNotInUse(childrel, "ALTER TABLE");
6236+
CheckAlterTableIsSafe(childrel);
62036237
table_close(childrel, NoLock);
62046238
}
62056239
list_free(inh);
@@ -6232,7 +6266,7 @@ ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
62326266
Relation childrel;
62336267

62346268
childrel = relation_open(childrelid, lockmode);
6235-
CheckTableNotInUse(childrel, "ALTER TABLE");
6269+
CheckAlterTableIsSafe(childrel);
62366270
ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
62376271
relation_close(childrel, NoLock);
62386272
}
@@ -6933,7 +6967,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
69336967

69346968
/* find_inheritance_children already got lock */
69356969
childrel = table_open(childrelid, NoLock);
6936-
CheckTableNotInUse(childrel, "ALTER TABLE");
6970+
CheckAlterTableIsSafe(childrel);
69376971

69386972
/* Find or create work queue entry for this table */
69396973
childtab = ATGetQueueEntry(wqueue, childrel);
@@ -8419,7 +8453,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
84198453

84208454
/* find_inheritance_children already got lock */
84218455
childrel = table_open(childrelid, NoLock);
8422-
CheckTableNotInUse(childrel, "ALTER TABLE");
8456+
CheckAlterTableIsSafe(childrel);
84238457

84248458
tuple = SearchSysCacheCopyAttName(childrelid, colName);
84258459
if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
@@ -8901,7 +8935,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
89018935

89028936
/* find_inheritance_children already got lock */
89038937
childrel = table_open(childrelid, NoLock);
8904-
CheckTableNotInUse(childrel, "ALTER TABLE");
8938+
CheckAlterTableIsSafe(childrel);
89058939

89068940
/* Find or create work queue entry for this table */
89078941
childtab = ATGetQueueEntry(wqueue, childrel);
@@ -9651,7 +9685,7 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
96519685
referenced;
96529686
ListCell *cell;
96539687

9654-
CheckTableNotInUse(partition, "ALTER TABLE");
9688+
CheckAlterTableIsSafe(partition);
96559689

96569690
attmap = build_attrmap_by_name(RelationGetDescr(partition),
96579691
RelationGetDescr(rel));
@@ -11527,7 +11561,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
1152711561

1152811562
/* Must match lock taken by RemoveTriggerById: */
1152911563
frel = table_open(con->confrelid, AccessExclusiveLock);
11530-
CheckTableNotInUse(frel, "ALTER TABLE");
11564+
CheckAlterTableIsSafe(frel);
1153111565
table_close(frel, NoLock);
1153211566
}
1153311567

@@ -11605,7 +11639,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
1160511639

1160611640
/* find_inheritance_children already got lock */
1160711641
childrel = table_open(childrelid, NoLock);
11608-
CheckTableNotInUse(childrel, "ALTER TABLE");
11642+
CheckAlterTableIsSafe(childrel);
1160911643

1161011644
ScanKeyInit(&skey[0],
1161111645
Anum_pg_constraint_conrelid,
@@ -11908,7 +11942,7 @@ ATPrepAlterColumnType(List **wqueue,
1190811942

1190911943
/* find_all_inheritors already got lock */
1191011944
childrel = relation_open(childrelid, NoLock);
11911-
CheckTableNotInUse(childrel, "ALTER TABLE");
11945+
CheckAlterTableIsSafe(childrel);
1191211946

1191311947
/*
1191411948
* Verify that the child doesn't have any inherited definitions of

0 commit comments

Comments
 (0)