Skip to content

Commit 7c4ac65

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 1d4ea13 commit 7c4ac65

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
@@ -355,6 +355,7 @@ static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
355355
static void validateForeignKeyConstraint(char *conname,
356356
Relation rel, Relation pkrel,
357357
Oid pkindOid, Oid constraintOid);
358+
static void CheckAlterTableIsSafe(Relation rel);
358359
static void ATController(AlterTableStmt *parsetree,
359360
Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
360361
AlterTableUtilityContext *context);
@@ -3697,6 +3698,37 @@ CheckTableNotInUse(Relation rel, const char *stmt)
36973698
stmt, RelationGetRelationName(rel))));
36983699
}
36993700

3701+
/*
3702+
* CheckAlterTableIsSafe
3703+
* Verify that it's safe to allow ALTER TABLE on this relation.
3704+
*
3705+
* This consists of CheckTableNotInUse() plus a check that the relation
3706+
* isn't another session's temp table. We must split out the temp-table
3707+
* check because there are callers of CheckTableNotInUse() that don't want
3708+
* that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
3709+
* an orphaned temp schema.) Compare truncate_check_activity().
3710+
*/
3711+
static void
3712+
CheckAlterTableIsSafe(Relation rel)
3713+
{
3714+
/*
3715+
* Don't allow ALTER on temp tables of other backends. Their local buffer
3716+
* manager is not going to cope if we need to change the table's contents.
3717+
* Even if we don't, there may be optimizations that assume temp tables
3718+
* aren't subject to such interference.
3719+
*/
3720+
if (RELATION_IS_OTHER_TEMP(rel))
3721+
ereport(ERROR,
3722+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3723+
errmsg("cannot alter temporary tables of other sessions")));
3724+
3725+
/*
3726+
* Also check for active uses of the relation in the current transaction,
3727+
* including open scans and pending AFTER trigger events.
3728+
*/
3729+
CheckTableNotInUse(rel, "ALTER TABLE");
3730+
}
3731+
37003732
/*
37013733
* AlterTableLookupRelation
37023734
* Look up, and lock, the OID for the relation named by an alter table
@@ -3771,7 +3803,7 @@ AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
37713803
/* Caller is required to provide an adequate lock. */
37723804
rel = relation_open(context->relid, NoLock);
37733805

3774-
CheckTableNotInUse(rel, "ALTER TABLE");
3806+
CheckAlterTableIsSafe(rel);
37753807

37763808
ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
37773809
}
@@ -5092,7 +5124,9 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
50925124

50935125
/*
50945126
* Don't allow rewrite on temp tables of other backends ... their
5095-
* local buffer manager is not going to cope.
5127+
* local buffer manager is not going to cope. (This is redundant
5128+
* with the check in CheckAlterTableIsSafe, but for safety we'll
5129+
* check here too.)
50965130
*/
50975131
if (RELATION_IS_OTHER_TEMP(OldHeap))
50985132
ereport(ERROR,
@@ -5835,7 +5869,7 @@ ATSimpleRecursion(List **wqueue, Relation rel,
58355869
continue;
58365870
/* find_all_inheritors already got lock */
58375871
childrel = relation_open(childrelid, NoLock);
5838-
CheckTableNotInUse(childrel, "ALTER TABLE");
5872+
CheckAlterTableIsSafe(childrel);
58395873
ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
58405874
relation_close(childrel, NoLock);
58415875
}
@@ -5844,7 +5878,7 @@ ATSimpleRecursion(List **wqueue, Relation rel,
58445878

58455879
/*
58465880
* Obtain list of partitions of the given table, locking them all at the given
5847-
* lockmode and ensuring that they all pass CheckTableNotInUse.
5881+
* lockmode and ensuring that they all pass CheckAlterTableIsSafe.
58485882
*
58495883
* This function is a no-op if the given relation is not a partitioned table;
58505884
* in particular, nothing is done if it's a legacy inheritance parent.
@@ -5865,7 +5899,7 @@ ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
58655899

58665900
/* find_all_inheritors already got lock */
58675901
childrel = table_open(lfirst_oid(cell), NoLock);
5868-
CheckTableNotInUse(childrel, "ALTER TABLE");
5902+
CheckAlterTableIsSafe(childrel);
58695903
table_close(childrel, NoLock);
58705904
}
58715905
list_free(inh);
@@ -5898,7 +5932,7 @@ ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
58985932
Relation childrel;
58995933

59005934
childrel = relation_open(childrelid, lockmode);
5901-
CheckTableNotInUse(childrel, "ALTER TABLE");
5935+
CheckAlterTableIsSafe(childrel);
59025936
ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
59035937
relation_close(childrel, NoLock);
59045938
}
@@ -6591,7 +6625,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
65916625

65926626
/* find_inheritance_children already got lock */
65936627
childrel = table_open(childrelid, NoLock);
6594-
CheckTableNotInUse(childrel, "ALTER TABLE");
6628+
CheckAlterTableIsSafe(childrel);
65956629

65966630
/* Find or create work queue entry for this table */
65976631
childtab = ATGetQueueEntry(wqueue, childrel);
@@ -8050,7 +8084,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
80508084

80518085
/* find_inheritance_children already got lock */
80528086
childrel = table_open(childrelid, NoLock);
8053-
CheckTableNotInUse(childrel, "ALTER TABLE");
8087+
CheckAlterTableIsSafe(childrel);
80548088

80558089
tuple = SearchSysCacheCopyAttName(childrelid, colName);
80568090
if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
@@ -8508,7 +8542,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
85088542

85098543
/* find_inheritance_children already got lock */
85108544
childrel = table_open(childrelid, NoLock);
8511-
CheckTableNotInUse(childrel, "ALTER TABLE");
8545+
CheckAlterTableIsSafe(childrel);
85128546

85138547
/* Find or create work queue entry for this table */
85148548
childtab = ATGetQueueEntry(wqueue, childrel);
@@ -9258,7 +9292,7 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
92589292
referenced;
92599293
ListCell *cell;
92609294

9261-
CheckTableNotInUse(partition, "ALTER TABLE");
9295+
CheckAlterTableIsSafe(partition);
92629296

92639297
attmap = build_attrmap_by_name(RelationGetDescr(partition),
92649298
RelationGetDescr(rel));
@@ -11131,7 +11165,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
1113111165

1113211166
/* Must match lock taken by RemoveTriggerById: */
1113311167
frel = table_open(con->confrelid, AccessExclusiveLock);
11134-
CheckTableNotInUse(frel, "ALTER TABLE");
11168+
CheckAlterTableIsSafe(frel);
1113511169
table_close(frel, NoLock);
1113611170
}
1113711171

@@ -11209,7 +11243,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
1120911243

1121011244
/* find_inheritance_children already got lock */
1121111245
childrel = table_open(childrelid, NoLock);
11212-
CheckTableNotInUse(childrel, "ALTER TABLE");
11246+
CheckAlterTableIsSafe(childrel);
1121311247

1121411248
ScanKeyInit(&skey[0],
1121511249
Anum_pg_constraint_conrelid,
@@ -11512,7 +11546,7 @@ ATPrepAlterColumnType(List **wqueue,
1151211546

1151311547
/* find_all_inheritors already got lock */
1151411548
childrel = relation_open(childrelid, NoLock);
11515-
CheckTableNotInUse(childrel, "ALTER TABLE");
11549+
CheckAlterTableIsSafe(childrel);
1151611550

1151711551
/*
1151811552
* Verify that the child doesn't have any inherited definitions of

0 commit comments

Comments
 (0)