diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 241cc8e68e..7ab3a51136 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -355,6 +355,7 @@ static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId, static void validateForeignKeyConstraint(char *conname, Relation rel, Relation pkrel, Oid pkindOid, Oid constraintOid); +static void CheckAlterTableIsSafe(Relation rel); static void ATController(AlterTableStmt *parsetree, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context); @@ -3697,6 +3698,37 @@ CheckTableNotInUse(Relation rel, const char *stmt) stmt, RelationGetRelationName(rel)))); } +/* + * CheckAlterTableIsSafe + * Verify that it's safe to allow ALTER TABLE on this relation. + * + * This consists of CheckTableNotInUse() plus a check that the relation + * isn't another session's temp table. We must split out the temp-table + * check because there are callers of CheckTableNotInUse() that don't want + * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out + * an orphaned temp schema.) Compare truncate_check_activity(). + */ +static void +CheckAlterTableIsSafe(Relation rel) +{ + /* + * Don't allow ALTER on temp tables of other backends. Their local buffer + * manager is not going to cope if we need to change the table's contents. + * Even if we don't, there may be optimizations that assume temp tables + * aren't subject to such interference. + */ + if (RELATION_IS_OTHER_TEMP(rel)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot alter temporary tables of other sessions"))); + + /* + * Also check for active uses of the relation in the current transaction, + * including open scans and pending AFTER trigger events. + */ + CheckTableNotInUse(rel, "ALTER TABLE"); +} + /* * AlterTableLookupRelation * Look up, and lock, the OID for the relation named by an alter table @@ -3771,7 +3803,7 @@ AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode, /* Caller is required to provide an adequate lock. */ rel = relation_open(context->relid, NoLock); - CheckTableNotInUse(rel, "ALTER TABLE"); + CheckAlterTableIsSafe(rel); ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context); } @@ -5092,7 +5124,9 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, /* * Don't allow rewrite on temp tables of other backends ... their - * local buffer manager is not going to cope. + * local buffer manager is not going to cope. (This is redundant + * with the check in CheckAlterTableIsSafe, but for safety we'll + * check here too.) */ if (RELATION_IS_OTHER_TEMP(OldHeap)) ereport(ERROR, @@ -5835,7 +5869,7 @@ ATSimpleRecursion(List **wqueue, Relation rel, continue; /* find_all_inheritors already got lock */ childrel = relation_open(childrelid, NoLock); - CheckTableNotInUse(childrel, "ALTER TABLE"); + CheckAlterTableIsSafe(childrel); ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context); relation_close(childrel, NoLock); } @@ -5844,7 +5878,7 @@ ATSimpleRecursion(List **wqueue, Relation rel, /* * Obtain list of partitions of the given table, locking them all at the given - * lockmode and ensuring that they all pass CheckTableNotInUse. + * lockmode and ensuring that they all pass CheckAlterTableIsSafe. * * This function is a no-op if the given relation is not a partitioned table; * in particular, nothing is done if it's a legacy inheritance parent. @@ -5865,7 +5899,7 @@ ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode) /* find_all_inheritors already got lock */ childrel = table_open(lfirst_oid(cell), NoLock); - CheckTableNotInUse(childrel, "ALTER TABLE"); + CheckAlterTableIsSafe(childrel); table_close(childrel, NoLock); } list_free(inh); @@ -5898,7 +5932,7 @@ ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, Relation childrel; childrel = relation_open(childrelid, lockmode); - CheckTableNotInUse(childrel, "ALTER TABLE"); + CheckAlterTableIsSafe(childrel); ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context); relation_close(childrel, NoLock); } @@ -6591,7 +6625,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, /* find_inheritance_children already got lock */ childrel = table_open(childrelid, NoLock); - CheckTableNotInUse(childrel, "ALTER TABLE"); + CheckAlterTableIsSafe(childrel); /* Find or create work queue entry for this table */ childtab = ATGetQueueEntry(wqueue, childrel); @@ -8050,7 +8084,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, /* find_inheritance_children already got lock */ childrel = table_open(childrelid, NoLock); - CheckTableNotInUse(childrel, "ALTER TABLE"); + CheckAlterTableIsSafe(childrel); tuple = SearchSysCacheCopyAttName(childrelid, colName); if (!HeapTupleIsValid(tuple)) /* shouldn't happen */ @@ -8508,7 +8542,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, /* find_inheritance_children already got lock */ childrel = table_open(childrelid, NoLock); - CheckTableNotInUse(childrel, "ALTER TABLE"); + CheckAlterTableIsSafe(childrel); /* Find or create work queue entry for this table */ childtab = ATGetQueueEntry(wqueue, childrel); @@ -9258,7 +9292,7 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, referenced; ListCell *cell; - CheckTableNotInUse(partition, "ALTER TABLE"); + CheckAlterTableIsSafe(partition); attmap = build_attrmap_by_name(RelationGetDescr(partition), RelationGetDescr(rel)); @@ -11131,7 +11165,7 @@ ATExecDropConstraint(Relation rel, const char *constrName, /* Must match lock taken by RemoveTriggerById: */ frel = table_open(con->confrelid, AccessExclusiveLock); - CheckTableNotInUse(frel, "ALTER TABLE"); + CheckAlterTableIsSafe(frel); table_close(frel, NoLock); } @@ -11209,7 +11243,7 @@ ATExecDropConstraint(Relation rel, const char *constrName, /* find_inheritance_children already got lock */ childrel = table_open(childrelid, NoLock); - CheckTableNotInUse(childrel, "ALTER TABLE"); + CheckAlterTableIsSafe(childrel); ScanKeyInit(&skey[0], Anum_pg_constraint_conrelid, @@ -11512,7 +11546,7 @@ ATPrepAlterColumnType(List **wqueue, /* find_all_inheritors already got lock */ childrel = relation_open(childrelid, NoLock); - CheckTableNotInUse(childrel, "ALTER TABLE"); + CheckAlterTableIsSafe(childrel); /* * Verify that the child doesn't have any inherited definitions of