88 *
99 *
1010 * IDENTIFICATION
11- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.238 2008/01/01 19:45:49 momjian Exp $
11+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.239 2008/01/02 23:34:42 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
@@ -194,6 +194,7 @@ static void validateForeignKeyConstraint(FkConstraint *fkconstraint,
194194 Relation rel , Relation pkrel , Oid constraintOid );
195195static void createForeignKeyTriggers (Relation rel , FkConstraint * fkconstraint ,
196196 Oid constraintOid );
197+ static void CheckTableNotInUse (Relation rel );
197198static void ATController (Relation rel , List * cmds , bool recurse );
198199static void ATPrepCmd (List * * wqueue , Relation rel , AlterTableCmd * cmd ,
199200 bool recurse , bool recursing );
@@ -598,13 +599,6 @@ ExecuteTruncate(TruncateStmt *stmt)
598599 heap_truncate_check_FKs (rels , false);
599600#endif
600601
601- /*
602- * Also check for pending AFTER trigger events on the target relations. We
603- * can't just leave those be, since they will try to fetch tuples that the
604- * TRUNCATE removes.
605- */
606- AfterTriggerCheckTruncate (relids );
607-
608602 /*
609603 * OK, truncate each table.
610604 */
@@ -685,6 +679,17 @@ truncate_check_rel(Relation rel)
685679 ereport (ERROR ,
686680 (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
687681 errmsg ("cannot truncate temporary tables of other sessions" )));
682+
683+ /*
684+ * Also check for pending AFTER trigger events on the relation. We can't
685+ * just leave those be, since they will try to fetch tuples that the
686+ * TRUNCATE removes.
687+ */
688+ if (AfterTriggerPendingOnRel (RelationGetRelid (rel )))
689+ ereport (ERROR ,
690+ (errcode (ERRCODE_OBJECT_IN_USE ),
691+ errmsg ("cannot truncate table \"%s\" because it has pending trigger events" ,
692+ RelationGetRelationName (rel ))));
688693}
689694
690695/*----------
@@ -1749,28 +1754,47 @@ void
17491754AlterTable (AlterTableStmt * stmt )
17501755{
17511756 Relation rel = relation_openrv (stmt -> relation , AccessExclusiveLock );
1757+
1758+ CheckTableNotInUse (rel );
1759+
1760+ ATController (rel , stmt -> cmds , interpretInhOption (stmt -> relation -> inhOpt ));
1761+ }
1762+
1763+ /*
1764+ * Disallow ALTER TABLE when the current backend has any open reference to
1765+ * it besides the one we just got (such as an open cursor or active plan);
1766+ * our AccessExclusiveLock doesn't protect us against stomping on our own
1767+ * foot, only other people's feet!
1768+ *
1769+ * Note: the only case known to cause serious trouble is ALTER COLUMN TYPE,
1770+ * and some changes are obviously pretty benign, so this could possibly be
1771+ * relaxed to only error out for certain types of alterations. But the
1772+ * use-case for allowing any of these things is not obvious, so we won't
1773+ * work hard at it for now.
1774+ *
1775+ * We also reject ALTER TABLE if there are any pending AFTER trigger events
1776+ * for the rel. This is certainly necessary for the rewriting variants of
1777+ * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
1778+ * events would try to fetch the wrong tuples. It might be overly cautious
1779+ * in other cases, but again it seems better to err on the side of paranoia.
1780+ */
1781+ static void
1782+ CheckTableNotInUse (Relation rel )
1783+ {
17521784 int expected_refcnt ;
17531785
1754- /*
1755- * Disallow ALTER TABLE when the current backend has any open reference to
1756- * it besides the one we just got (such as an open cursor or active plan);
1757- * our AccessExclusiveLock doesn't protect us against stomping on our own
1758- * foot, only other people's feet!
1759- *
1760- * Note: the only case known to cause serious trouble is ALTER COLUMN
1761- * TYPE, and some changes are obviously pretty benign, so this could
1762- * possibly be relaxed to only error out for certain types of alterations.
1763- * But the use-case for allowing any of these things is not obvious, so we
1764- * won't work hard at it for now.
1765- */
17661786 expected_refcnt = rel -> rd_isnailed ? 2 : 1 ;
17671787 if (rel -> rd_refcnt != expected_refcnt )
17681788 ereport (ERROR ,
17691789 (errcode (ERRCODE_OBJECT_IN_USE ),
17701790 errmsg ("relation \"%s\" is being used by active queries in this session" ,
17711791 RelationGetRelationName (rel ))));
17721792
1773- ATController (rel , stmt -> cmds , interpretInhOption (stmt -> relation -> inhOpt ));
1793+ if (AfterTriggerPendingOnRel (RelationGetRelid (rel )))
1794+ ereport (ERROR ,
1795+ (errcode (ERRCODE_OBJECT_IN_USE ),
1796+ errmsg ("cannot alter table \"%s\" because it has pending trigger events" ,
1797+ RelationGetRelationName (rel ))));
17741798}
17751799
17761800/*
@@ -1781,7 +1805,8 @@ AlterTable(AlterTableStmt *stmt)
17811805 * We do not reject if the relation is already open, because it's quite
17821806 * likely that one or more layers of caller have it open. That means it
17831807 * is unsafe to use this entry point for alterations that could break
1784- * existing query plans.
1808+ * existing query plans. On the assumption it's not used for such, we
1809+ * don't have to reject pending AFTER triggers, either.
17851810 */
17861811void
17871812AlterTableInternal (Oid relid , List * cmds , bool recurse )
@@ -2784,12 +2809,7 @@ ATSimpleRecursion(List **wqueue, Relation rel,
27842809 if (childrelid == relid )
27852810 continue ;
27862811 childrel = relation_open (childrelid , AccessExclusiveLock );
2787- /* check for child relation in use in this session */
2788- if (childrel -> rd_refcnt != 1 )
2789- ereport (ERROR ,
2790- (errcode (ERRCODE_OBJECT_IN_USE ),
2791- errmsg ("relation \"%s\" is being used by active queries in this session" ,
2792- RelationGetRelationName (childrel ))));
2812+ CheckTableNotInUse (childrel );
27932813 ATPrepCmd (wqueue , childrel , cmd , false, true);
27942814 relation_close (childrel , NoLock );
27952815 }
@@ -2821,12 +2841,7 @@ ATOneLevelRecursion(List **wqueue, Relation rel,
28212841 Relation childrel ;
28222842
28232843 childrel = relation_open (childrelid , AccessExclusiveLock );
2824- /* check for child relation in use in this session */
2825- if (childrel -> rd_refcnt != 1 )
2826- ereport (ERROR ,
2827- (errcode (ERRCODE_OBJECT_IN_USE ),
2828- errmsg ("relation \"%s\" is being used by active queries in this session" ,
2829- RelationGetRelationName (childrel ))));
2844+ CheckTableNotInUse (childrel );
28302845 ATPrepCmd (wqueue , childrel , cmd , true, true);
28312846 relation_close (childrel , NoLock );
28322847 }
@@ -3655,12 +3670,7 @@ ATExecDropColumn(Relation rel, const char *colName,
36553670 Form_pg_attribute childatt ;
36563671
36573672 childrel = heap_open (childrelid , AccessExclusiveLock );
3658- /* check for child relation in use in this session */
3659- if (childrel -> rd_refcnt != 1 )
3660- ereport (ERROR ,
3661- (errcode (ERRCODE_OBJECT_IN_USE ),
3662- errmsg ("relation \"%s\" is being used by active queries in this session" ,
3663- RelationGetRelationName (childrel ))));
3673+ CheckTableNotInUse (childrel );
36643674
36653675 tuple = SearchSysCacheCopyAttName (childrelid , colName );
36663676 if (!HeapTupleIsValid (tuple )) /* shouldn't happen */
0 commit comments