Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 0688d84

Browse files
committed
Add checks to TRUNCATE, CLUSTER, and REINDEX to prevent performing these
operations when the current transaction has any open references to the target relation or index (implying it has an active query using the relation). The need for this was previously recognized in connection with ALTER TABLE, but anything that summarily eliminates tuples or moves them around would confuse an active scan. While this patch does not in itself fix bug #3883 (the deadlock would happen before the new check fires), it will discourage people from attempting the sequence of operations that creates a deadlock risk, so it's at least a partial response to that problem. In passing, add a previously-missing check to REINDEX to prevent trying to reindex another backend's temp table. This isn't a security problem since only a superuser would get past the schema permission checks, but if we are testing for this in other utility commands then surely REINDEX should too.
1 parent 47df4f6 commit 0688d84

File tree

4 files changed

+83
-62
lines changed

4 files changed

+83
-62
lines changed

src/backend/catalog/index.c

+18-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.291 2008/01/14 01:39:09 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.292 2008/01/30 19:46:48 tgl Exp $
1212
*
1313
*
1414
* INTERFACE ROUTINES
@@ -33,11 +33,13 @@
3333
#include "catalog/heap.h"
3434
#include "catalog/index.h"
3535
#include "catalog/indexing.h"
36+
#include "catalog/namespace.h"
3637
#include "catalog/pg_constraint.h"
3738
#include "catalog/pg_operator.h"
3839
#include "catalog/pg_opclass.h"
3940
#include "catalog/pg_tablespace.h"
4041
#include "catalog/pg_type.h"
42+
#include "commands/tablecmds.h"
4143
#include "executor/executor.h"
4244
#include "miscadmin.h"
4345
#include "optimizer/clauses.h"
@@ -2219,6 +2221,21 @@ reindex_index(Oid indexId)
22192221
*/
22202222
iRel = index_open(indexId, AccessExclusiveLock);
22212223

2224+
/*
2225+
* Don't allow reindex on temp tables of other backends ... their local
2226+
* buffer manager is not going to cope.
2227+
*/
2228+
if (isOtherTempNamespace(RelationGetNamespace(iRel)))
2229+
ereport(ERROR,
2230+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2231+
errmsg("cannot reindex temporary tables of other sessions")));
2232+
2233+
/*
2234+
* Also check for active uses of the index in the current transaction;
2235+
* we don't want to reindex underneath an open indexscan.
2236+
*/
2237+
CheckTableNotInUse(iRel, "REINDEX INDEX");
2238+
22222239
/*
22232240
* If it's a shared index, we must do inplace processing (because we have
22242241
* no way to update relfilenode in other databases). Otherwise we can do

src/backend/commands/cluster.c

+5-9
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*
1212
*
1313
* IDENTIFICATION
14-
* $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.168 2008/01/15 21:20:28 tgl Exp $
14+
* $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.169 2008/01/30 19:46:48 tgl Exp $
1515
*
1616
*-------------------------------------------------------------------------
1717
*/
@@ -30,6 +30,7 @@
3030
#include "catalog/namespace.h"
3131
#include "catalog/toasting.h"
3232
#include "commands/cluster.h"
33+
#include "commands/tablecmds.h"
3334
#include "commands/trigger.h"
3435
#include "commands/vacuum.h"
3536
#include "miscadmin.h"
@@ -459,15 +460,10 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck)
459460
errmsg("cannot cluster temporary tables of other sessions")));
460461

461462
/*
462-
* Also check for pending AFTER trigger events on the relation. We can't
463-
* just leave those be, since they will try to fetch tuples that the
464-
* CLUSTER has moved to new TIDs.
463+
* Also check for active uses of the relation in the current transaction,
464+
* including open scans and pending AFTER trigger events.
465465
*/
466-
if (AfterTriggerPendingOnRel(RelationGetRelid(OldHeap)))
467-
ereport(ERROR,
468-
(errcode(ERRCODE_OBJECT_IN_USE),
469-
errmsg("cannot cluster table \"%s\" because it has pending trigger events",
470-
RelationGetRelationName(OldHeap))));
466+
CheckTableNotInUse(OldHeap, "CLUSTER");
471467

472468
/* Drop relcache refcnt on OldIndex, but keep lock */
473469
index_close(OldIndex, NoLock);

src/backend/commands/tablecmds.c

+57-51
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* 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 $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -194,7 +194,6 @@ static void validateForeignKeyConstraint(FkConstraint *fkconstraint,
194194
Relation rel, Relation pkrel, Oid constraintOid);
195195
static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
196196
Oid constraintOid);
197-
static void CheckTableNotInUse(Relation rel);
198197
static void ATController(Relation rel, List *cmds, bool recurse);
199198
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
200199
bool recurse, bool recursing);
@@ -681,15 +680,10 @@ truncate_check_rel(Relation rel)
681680
errmsg("cannot truncate temporary tables of other sessions")));
682681

683682
/*
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.
687685
*/
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");
693687
}
694688

695689
/*----------
@@ -1728,6 +1722,55 @@ renamerel(Oid myrelid, const char *newrelname, ObjectType reltype)
17281722
relation_close(targetrelation, NoLock);
17291723
}
17301724

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+
17311774
/*
17321775
* AlterTable
17331776
* Execute ALTER TABLE, which can be a list of subcommands
@@ -1766,48 +1809,11 @@ AlterTable(AlterTableStmt *stmt)
17661809
{
17671810
Relation rel = relation_openrv(stmt->relation, AccessExclusiveLock);
17681811

1769-
CheckTableNotInUse(rel);
1812+
CheckTableNotInUse(rel, "ALTER TABLE");
17701813

17711814
ATController(rel, stmt->cmds, interpretInhOption(stmt->relation->inhOpt));
17721815
}
17731816

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-
18111817
/*
18121818
* AlterTableInternal
18131819
*
@@ -2820,7 +2826,7 @@ ATSimpleRecursion(List **wqueue, Relation rel,
28202826
if (childrelid == relid)
28212827
continue;
28222828
childrel = relation_open(childrelid, AccessExclusiveLock);
2823-
CheckTableNotInUse(childrel);
2829+
CheckTableNotInUse(childrel, "ALTER TABLE");
28242830
ATPrepCmd(wqueue, childrel, cmd, false, true);
28252831
relation_close(childrel, NoLock);
28262832
}
@@ -2852,7 +2858,7 @@ ATOneLevelRecursion(List **wqueue, Relation rel,
28522858
Relation childrel;
28532859

28542860
childrel = relation_open(childrelid, AccessExclusiveLock);
2855-
CheckTableNotInUse(childrel);
2861+
CheckTableNotInUse(childrel, "ALTER TABLE");
28562862
ATPrepCmd(wqueue, childrel, cmd, true, true);
28572863
relation_close(childrel, NoLock);
28582864
}
@@ -3681,7 +3687,7 @@ ATExecDropColumn(Relation rel, const char *colName,
36813687
Form_pg_attribute childatt;
36823688

36833689
childrel = heap_open(childrelid, AccessExclusiveLock);
3684-
CheckTableNotInUse(childrel);
3690+
CheckTableNotInUse(childrel, "ALTER TABLE");
36853691

36863692
tuple = SearchSysCacheCopyAttName(childrelid, colName);
36873693
if (!HeapTupleIsValid(tuple)) /* shouldn't happen */

src/include/commands/tablecmds.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.36 2008/01/01 19:45:57 momjian Exp $
10+
* $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.37 2008/01/30 19:46:48 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -34,6 +34,8 @@ extern void AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
3434
Oid oldNspOid, Oid newNspOid,
3535
bool hasDependEntry);
3636

37+
extern void CheckTableNotInUse(Relation rel, const char *stmt);
38+
3739
extern void ExecuteTruncate(TruncateStmt *stmt);
3840

3941
extern void renameatt(Oid myrelid,

0 commit comments

Comments
 (0)