Skip to content

Commit 3c71cb4

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 a160e92 commit 3c71cb4

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
@@ -387,6 +387,7 @@ static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
387387
static void validateForeignKeyConstraint(char *conname,
388388
Relation rel, Relation pkrel,
389389
Oid pkindOid, Oid constraintOid);
390+
static void CheckAlterTableIsSafe(Relation rel);
390391
static void ATController(AlterTableStmt *parsetree,
391392
Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
392393
AlterTableUtilityContext *context);
@@ -4014,6 +4015,37 @@ CheckTableNotInUse(Relation rel, const char *stmt)
40144015
stmt, RelationGetRelationName(rel))));
40154016
}
40164017

4018+
/*
4019+
* CheckAlterTableIsSafe
4020+
* Verify that it's safe to allow ALTER TABLE on this relation.
4021+
*
4022+
* This consists of CheckTableNotInUse() plus a check that the relation
4023+
* isn't another session's temp table. We must split out the temp-table
4024+
* check because there are callers of CheckTableNotInUse() that don't want
4025+
* that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4026+
* an orphaned temp schema.) Compare truncate_check_activity().
4027+
*/
4028+
static void
4029+
CheckAlterTableIsSafe(Relation rel)
4030+
{
4031+
/*
4032+
* Don't allow ALTER on temp tables of other backends. Their local buffer
4033+
* manager is not going to cope if we need to change the table's contents.
4034+
* Even if we don't, there may be optimizations that assume temp tables
4035+
* aren't subject to such interference.
4036+
*/
4037+
if (RELATION_IS_OTHER_TEMP(rel))
4038+
ereport(ERROR,
4039+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4040+
errmsg("cannot alter temporary tables of other sessions")));
4041+
4042+
/*
4043+
* Also check for active uses of the relation in the current transaction,
4044+
* including open scans and pending AFTER trigger events.
4045+
*/
4046+
CheckTableNotInUse(rel, "ALTER TABLE");
4047+
}
4048+
40174049
/*
40184050
* AlterTableLookupRelation
40194051
* Look up, and lock, the OID for the relation named by an alter table
@@ -4088,7 +4120,7 @@ AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
40884120
/* Caller is required to provide an adequate lock. */
40894121
rel = relation_open(context->relid, NoLock);
40904122

4091-
CheckTableNotInUse(rel, "ALTER TABLE");
4123+
CheckAlterTableIsSafe(rel);
40924124

40934125
ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
40944126
}
@@ -5480,7 +5512,9 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
54805512

54815513
/*
54825514
* Don't allow rewrite on temp tables of other backends ... their
5483-
* local buffer manager is not going to cope.
5515+
* local buffer manager is not going to cope. (This is redundant
5516+
* with the check in CheckAlterTableIsSafe, but for safety we'll
5517+
* check here too.)
54845518
*/
54855519
if (RELATION_IS_OTHER_TEMP(OldHeap))
54865520
ereport(ERROR,
@@ -6348,7 +6382,7 @@ ATSimpleRecursion(List **wqueue, Relation rel,
63486382
continue;
63496383
/* find_all_inheritors already got lock */
63506384
childrel = relation_open(childrelid, NoLock);
6351-
CheckTableNotInUse(childrel, "ALTER TABLE");
6385+
CheckAlterTableIsSafe(childrel);
63526386
ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
63536387
relation_close(childrel, NoLock);
63546388
}
@@ -6357,7 +6391,7 @@ ATSimpleRecursion(List **wqueue, Relation rel,
63576391

63586392
/*
63596393
* Obtain list of partitions of the given table, locking them all at the given
6360-
* lockmode and ensuring that they all pass CheckTableNotInUse.
6394+
* lockmode and ensuring that they all pass CheckAlterTableIsSafe.
63616395
*
63626396
* This function is a no-op if the given relation is not a partitioned table;
63636397
* in particular, nothing is done if it's a legacy inheritance parent.
@@ -6378,7 +6412,7 @@ ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
63786412

63796413
/* find_all_inheritors already got lock */
63806414
childrel = table_open(lfirst_oid(cell), NoLock);
6381-
CheckTableNotInUse(childrel, "ALTER TABLE");
6415+
CheckAlterTableIsSafe(childrel);
63826416
table_close(childrel, NoLock);
63836417
}
63846418
list_free(inh);
@@ -6411,7 +6445,7 @@ ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
64116445
Relation childrel;
64126446

64136447
childrel = relation_open(childrelid, lockmode);
6414-
CheckTableNotInUse(childrel, "ALTER TABLE");
6448+
CheckAlterTableIsSafe(childrel);
64156449
ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
64166450
relation_close(childrel, NoLock);
64176451
}
@@ -7112,7 +7146,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
71127146

71137147
/* find_inheritance_children already got lock */
71147148
childrel = table_open(childrelid, NoLock);
7115-
CheckTableNotInUse(childrel, "ALTER TABLE");
7149+
CheckAlterTableIsSafe(childrel);
71167150

71177151
/* Find or create work queue entry for this table */
71187152
childtab = ATGetQueueEntry(wqueue, childrel);
@@ -8572,7 +8606,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
85728606

85738607
/* find_inheritance_children already got lock */
85748608
childrel = table_open(childrelid, NoLock);
8575-
CheckTableNotInUse(childrel, "ALTER TABLE");
8609+
CheckAlterTableIsSafe(childrel);
85768610

85778611
tuple = SearchSysCacheCopyAttName(childrelid, colName);
85788612
if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
@@ -9054,7 +9088,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
90549088

90559089
/* find_inheritance_children already got lock */
90569090
childrel = table_open(childrelid, NoLock);
9057-
CheckTableNotInUse(childrel, "ALTER TABLE");
9091+
CheckAlterTableIsSafe(childrel);
90589092

90599093
/* Find or create work queue entry for this table */
90609094
childtab = ATGetQueueEntry(wqueue, childrel);
@@ -9892,7 +9926,7 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
98929926
referenced;
98939927
ListCell *cell;
98949928

9895-
CheckTableNotInUse(partition, "ALTER TABLE");
9929+
CheckAlterTableIsSafe(partition);
98969930

98979931
attmap = build_attrmap_by_name(RelationGetDescr(partition),
98989932
RelationGetDescr(rel));
@@ -12019,7 +12053,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
1201912053

1202012054
/* Must match lock taken by RemoveTriggerById: */
1202112055
frel = table_open(con->confrelid, AccessExclusiveLock);
12022-
CheckTableNotInUse(frel, "ALTER TABLE");
12056+
CheckAlterTableIsSafe(frel);
1202312057
table_close(frel, NoLock);
1202412058
}
1202512059

@@ -12097,7 +12131,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
1209712131

1209812132
/* find_inheritance_children already got lock */
1209912133
childrel = table_open(childrelid, NoLock);
12100-
CheckTableNotInUse(childrel, "ALTER TABLE");
12134+
CheckAlterTableIsSafe(childrel);
1210112135

1210212136
ScanKeyInit(&skey[0],
1210312137
Anum_pg_constraint_conrelid,
@@ -12400,7 +12434,7 @@ ATPrepAlterColumnType(List **wqueue,
1240012434

1240112435
/* find_all_inheritors already got lock */
1240212436
childrel = relation_open(childrelid, NoLock);
12403-
CheckTableNotInUse(childrel, "ALTER TABLE");
12437+
CheckAlterTableIsSafe(childrel);
1240412438

1240512439
/*
1240612440
* Verify that the child doesn't have any inherited definitions of

0 commit comments

Comments
 (0)