|
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 | Relationrel,Relationpkrel,OidconstraintOid);
|
195 | 195 | staticvoidcreateForeignKeyTriggers(Relationrel,FkConstraint*fkconstraint,
|
196 | 196 | OidconstraintOid);
|
197 |
| -staticvoidCheckTableNotInUse(Relationrel); |
198 | 197 | staticvoidATController(Relationrel,List*cmds,boolrecurse);
|
199 | 198 | staticvoidATPrepCmd(List**wqueue,Relationrel,AlterTableCmd*cmd,
|
200 | 199 | boolrecurse,boolrecursing);
|
@@ -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(Relationrel,constchar*stmt) |
| 1752 | +{ |
| 1753 | +intexpected_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 | Relationrel=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 |
| -staticvoid |
1793 |
| -CheckTableNotInUse(Relationrel) |
1794 |
| -{ |
1795 |
| -intexpected_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 | Relationchildrel;
|
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_attributechildatt;
|
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 */
|
|