|
8 | 8 | * |
9 | 9 | * |
10 | 10 | * IDENTIFICATION |
11 | | - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.240 2008/01/17 18:56:54 tgl Exp $ |
| 11 | + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.241 2008/01/30 19:46:48 tgl Exp $ |
12 | 12 | * |
13 | 13 | *------------------------------------------------------------------------- |
14 | 14 | */ |
@@ -194,7 +194,6 @@ static void validateForeignKeyConstraint(FkConstraint *fkconstraint, |
194 | 194 | Relation rel, Relation pkrel, Oid constraintOid); |
195 | 195 | static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, |
196 | 196 | Oid constraintOid); |
197 | | -static void CheckTableNotInUse(Relation rel); |
198 | 197 | static void ATController(Relation rel, List *cmds, bool recurse); |
199 | 198 | static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, |
200 | 199 | bool recurse, bool recursing); |
@@ -681,15 +680,10 @@ truncate_check_rel(Relation rel) |
681 | 680 | errmsg("cannot truncate temporary tables of other sessions"))); |
682 | 681 |
|
683 | 682 | /* |
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. |
| 683 | + * Also check for active uses of the relation in the current transaction, |
| 684 | + * including open scans and pending AFTER trigger events. |
687 | 685 | */ |
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)))); |
| 686 | + CheckTableNotInUse(rel, "TRUNCATE"); |
693 | 687 | } |
694 | 688 |
|
695 | 689 | /*---------- |
@@ -1728,6 +1722,55 @@ renamerel(Oid myrelid, const char *newrelname, ObjectType reltype) |
1728 | 1722 | relation_close(targetrelation, NoLock); |
1729 | 1723 | } |
1730 | 1724 |
|
| 1725 | +/* |
| 1726 | + * Disallow ALTER TABLE (and similar commands) when the current backend has |
| 1727 | + * any open reference to the target table besides the one just acquired by |
| 1728 | + * the calling command; this implies there's an open cursor or active plan. |
| 1729 | + * We need this check because our AccessExclusiveLock doesn't protect us |
| 1730 | + * against stomping on our own foot, only other people's feet! |
| 1731 | + * |
| 1732 | + * For ALTER TABLE, the only case known to cause serious trouble is ALTER |
| 1733 | + * COLUMN TYPE, and some changes are obviously pretty benign, so this could |
| 1734 | + * possibly be relaxed to only error out for certain types of alterations. |
| 1735 | + * But the use-case for allowing any of these things is not obvious, so we |
| 1736 | + * won't work hard at it for now. |
| 1737 | + * |
| 1738 | + * We also reject these commands if there are any pending AFTER trigger events |
| 1739 | + * for the rel. This is certainly necessary for the rewriting variants of |
| 1740 | + * ALTER TABLE, because they don't preserve tuple TIDs and so the pending |
| 1741 | + * events would try to fetch the wrong tuples. It might be overly cautious |
| 1742 | + * in other cases, but again it seems better to err on the side of paranoia. |
| 1743 | + * |
| 1744 | + * REINDEX calls this with "rel" referencing the index to be rebuilt; here |
| 1745 | + * we are worried about active indexscans on the index. The trigger-event |
| 1746 | + * check can be skipped, since we are doing no damage to the parent table. |
| 1747 | + * |
| 1748 | + * The statement name (eg, "ALTER TABLE") is passed for use in error messages. |
| 1749 | + */ |
| 1750 | +void |
| 1751 | +CheckTableNotInUse(Relation rel, const char *stmt) |
| 1752 | +{ |
| 1753 | + int expected_refcnt; |
| 1754 | + |
| 1755 | + expected_refcnt = rel->rd_isnailed ? 2 : 1; |
| 1756 | + if (rel->rd_refcnt != expected_refcnt) |
| 1757 | + ereport(ERROR, |
| 1758 | + (errcode(ERRCODE_OBJECT_IN_USE), |
| 1759 | + /* translator: first %s is a SQL command, eg ALTER TABLE */ |
| 1760 | + errmsg("cannot %s \"%s\" because " |
| 1761 | + "it is being used by active queries in this session", |
| 1762 | + stmt, RelationGetRelationName(rel)))); |
| 1763 | + |
| 1764 | + if (rel->rd_rel->relkind != RELKIND_INDEX && |
| 1765 | + AfterTriggerPendingOnRel(RelationGetRelid(rel))) |
| 1766 | + ereport(ERROR, |
| 1767 | + (errcode(ERRCODE_OBJECT_IN_USE), |
| 1768 | + /* translator: first %s is a SQL command, eg ALTER TABLE */ |
| 1769 | + errmsg("cannot %s \"%s\" because " |
| 1770 | + "it has pending trigger events", |
| 1771 | + stmt, RelationGetRelationName(rel)))); |
| 1772 | +} |
| 1773 | + |
1731 | 1774 | /* |
1732 | 1775 | * AlterTable |
1733 | 1776 | * Execute ALTER TABLE, which can be a list of subcommands |
@@ -1766,48 +1809,11 @@ AlterTable(AlterTableStmt *stmt) |
1766 | 1809 | { |
1767 | 1810 | Relation rel = relation_openrv(stmt->relation, AccessExclusiveLock); |
1768 | 1811 |
|
1769 | | - CheckTableNotInUse(rel); |
| 1812 | + CheckTableNotInUse(rel, "ALTER TABLE"); |
1770 | 1813 |
|
1771 | 1814 | ATController(rel, stmt->cmds, interpretInhOption(stmt->relation->inhOpt)); |
1772 | 1815 | } |
1773 | 1816 |
|
1774 | | -/* |
1775 | | - * Disallow ALTER TABLE when the current backend has any open reference to |
1776 | | - * it besides the one we just got (such as an open cursor or active plan); |
1777 | | - * our AccessExclusiveLock doesn't protect us against stomping on our own |
1778 | | - * foot, only other people's feet! |
1779 | | - * |
1780 | | - * Note: the only case known to cause serious trouble is ALTER COLUMN TYPE, |
1781 | | - * and some changes are obviously pretty benign, so this could possibly be |
1782 | | - * relaxed to only error out for certain types of alterations. But the |
1783 | | - * use-case for allowing any of these things is not obvious, so we won't |
1784 | | - * work hard at it for now. |
1785 | | - * |
1786 | | - * We also reject ALTER TABLE if there are any pending AFTER trigger events |
1787 | | - * for the rel. This is certainly necessary for the rewriting variants of |
1788 | | - * ALTER TABLE, because they don't preserve tuple TIDs and so the pending |
1789 | | - * events would try to fetch the wrong tuples. It might be overly cautious |
1790 | | - * in other cases, but again it seems better to err on the side of paranoia. |
1791 | | - */ |
1792 | | -static void |
1793 | | -CheckTableNotInUse(Relation rel) |
1794 | | -{ |
1795 | | - int expected_refcnt; |
1796 | | - |
1797 | | - expected_refcnt = rel->rd_isnailed ? 2 : 1; |
1798 | | - if (rel->rd_refcnt != expected_refcnt) |
1799 | | - ereport(ERROR, |
1800 | | - (errcode(ERRCODE_OBJECT_IN_USE), |
1801 | | - errmsg("relation \"%s\" is being used by active queries in this session", |
1802 | | - RelationGetRelationName(rel)))); |
1803 | | - |
1804 | | - if (AfterTriggerPendingOnRel(RelationGetRelid(rel))) |
1805 | | - ereport(ERROR, |
1806 | | - (errcode(ERRCODE_OBJECT_IN_USE), |
1807 | | - errmsg("cannot alter table \"%s\" because it has pending trigger events", |
1808 | | - RelationGetRelationName(rel)))); |
1809 | | -} |
1810 | | - |
1811 | 1817 | /* |
1812 | 1818 | * AlterTableInternal |
1813 | 1819 | * |
@@ -2820,7 +2826,7 @@ ATSimpleRecursion(List **wqueue, Relation rel, |
2820 | 2826 | if (childrelid == relid) |
2821 | 2827 | continue; |
2822 | 2828 | childrel = relation_open(childrelid, AccessExclusiveLock); |
2823 | | - CheckTableNotInUse(childrel); |
| 2829 | + CheckTableNotInUse(childrel, "ALTER TABLE"); |
2824 | 2830 | ATPrepCmd(wqueue, childrel, cmd, false, true); |
2825 | 2831 | relation_close(childrel, NoLock); |
2826 | 2832 | } |
@@ -2852,7 +2858,7 @@ ATOneLevelRecursion(List **wqueue, Relation rel, |
2852 | 2858 | Relation childrel; |
2853 | 2859 |
|
2854 | 2860 | childrel = relation_open(childrelid, AccessExclusiveLock); |
2855 | | - CheckTableNotInUse(childrel); |
| 2861 | + CheckTableNotInUse(childrel, "ALTER TABLE"); |
2856 | 2862 | ATPrepCmd(wqueue, childrel, cmd, true, true); |
2857 | 2863 | relation_close(childrel, NoLock); |
2858 | 2864 | } |
@@ -3681,7 +3687,7 @@ ATExecDropColumn(Relation rel, const char *colName, |
3681 | 3687 | Form_pg_attribute childatt; |
3682 | 3688 |
|
3683 | 3689 | childrel = heap_open(childrelid, AccessExclusiveLock); |
3684 | | - CheckTableNotInUse(childrel); |
| 3690 | + CheckTableNotInUse(childrel, "ALTER TABLE"); |
3685 | 3691 |
|
3686 | 3692 | tuple = SearchSysCacheCopyAttName(childrelid, colName); |
3687 | 3693 | if (!HeapTupleIsValid(tuple)) /* shouldn't happen */ |
|
0 commit comments