|
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