1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-17 06:41:24 +03:00

Back-patch the 8.3 fix that prohibits TRUNCATE, CLUSTER, and REINDEX when the

current transaction has any open references to the target relation or index
(implying it has an active query using the relation).  Also back-patch the
8.2 fix that prohibits TRUNCATE and CLUSTER when there are pending
AFTER-trigger events.  Per suggestion from Heikki.
This commit is contained in:
Tom Lane 2008-05-27 21:14:00 +00:00
parent ea28271165
commit c59eef17c9
6 changed files with 125 additions and 6 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.219.2.3 2008/01/03 21:25:33 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.219.2.4 2008/05/27 21:14:00 tgl Exp $
*
*
* INTERFACE ROUTINES
@ -33,11 +33,13 @@
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_index.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/tablecmds.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "optimizer/clauses.h"
@ -1684,6 +1686,21 @@ reindex_index(Oid indexId)
/* Open and lock the parent heap relation */
heapRelation = heap_open(heapId, AccessExclusiveLock);
/*
* Don't allow reindex on temp tables of other backends ... their local
* buffer manager is not going to cope.
*/
if (isOtherTempNamespace(RelationGetNamespace(iRel)))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot reindex temporary tables of other sessions")));
/*
* Also check for active uses of the index in the current transaction;
* we don't want to reindex underneath an open indexscan.
*/
CheckTableNotInUse(iRel, "REINDEX INDEX");
SetReindexProcessing(heapId, indexId);
/*

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.116.2.3 2007/09/12 15:16:24 alvherre Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.116.2.4 2008/05/27 21:14:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -398,6 +398,12 @@ cluster_rel(RelToCluster *rvtc, bool recheck)
errmsg("\"%s\" is a system catalog",
RelationGetRelationName(OldHeap))));
/*
* Also check for active uses of the relation in the current transaction,
* including open scans and pending AFTER trigger events.
*/
CheckTableNotInUse(OldHeap, "CLUSTER");
/* Drop relcache refcnt on OldIndex, but keep lock */
index_close(OldIndex);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.91.2.3 2008/05/09 22:38:05 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.91.2.4 2008/05/27 21:14:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -397,6 +397,12 @@ TruncateRelation(const RangeVar *relation)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot truncate temporary tables of other sessions")));
/*
* Also check for active uses of the relation in the current
* transaction, including open scans and pending AFTER trigger events.
*/
CheckTableNotInUse(rel, "TRUNCATE");
/*
* Don't allow truncate on tables which are referenced by foreign keys
*/
@ -1592,6 +1598,48 @@ update_ri_trigger_args(Oid relid,
CommandCounterIncrement();
}
/*
* Disallow TRUNCATE (and similar commands) when the current backend has
* any open reference to the target table besides the one just acquired by
* the calling command; this implies there's an open cursor or active plan.
* We need this check because our AccessExclusiveLock doesn't protect us
* against stomping on our own foot, only other people's feet!
*
* We also reject these commands if there are any pending AFTER trigger events
* for the rel. This is certainly necessary for CLUSTER, because it does not
* preserve tuple TIDs and so the pending events would try to fetch the wrong
* tuples. It might be overly cautious in other cases, but again it seems
* better to err on the side of paranoia.
*
* REINDEX calls this with "rel" referencing the index to be rebuilt; here
* we are worried about active indexscans on the index. The trigger-event
* check can be skipped, since we are doing no damage to the parent table.
*
* The statement name (eg, "TRUNCATE") is passed for use in error messages.
*/
void
CheckTableNotInUse(Relation rel, const char *stmt)
{
int expected_refcnt;
expected_refcnt = rel->rd_isnailed ? 2 : 1;
if (rel->rd_refcnt != expected_refcnt)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_IN_USE),
/* translator: first %s is a SQL command, eg ALTER TABLE */
errmsg("cannot %s \"%s\" because "
"it is being used by active queries in this session",
stmt, RelationGetRelationName(rel))));
if (rel->rd_rel->relkind != RELKIND_INDEX &&
AfterTriggerPendingOnRel(RelationGetRelid(rel)))
ereport(ERROR,
(errcode(ERRCODE_OBJECT_IN_USE),
/* translator: first %s is a SQL command, eg ALTER TABLE */
errmsg("cannot %s \"%s\" because "
"it has pending trigger events",
stmt, RelationGetRelationName(rel))));
}
/* ----------------
* AlterTableAddColumn

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.159.2.2 2006/01/12 21:49:31 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.159.2.3 2008/05/27 21:14:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -2365,6 +2365,49 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt)
deferredTriggers->deftrig_events_imm = NULL;
}
/* ----------
* AfterTriggerPendingOnRel()
* Test to see if there are any pending after-trigger events for rel.
*
* This is used by TRUNCATE, CLUSTER, ALTER TABLE, etc to detect whether
* it is unsafe to perform major surgery on a relation. Note that only
* local pending events are examined. We assume that having exclusive lock
* on a rel guarantees there are no unserviced events in other backends ---
* but having a lock does not prevent there being such events in our own.
*
* In some scenarios it'd be reasonable to remove pending events (more
* specifically, mark them DONE by the current subxact) but without a lot
* of knowledge of the trigger semantics we can't do this in general.
* ----------
*/
bool
AfterTriggerPendingOnRel(Oid relid)
{
DeferredTriggerEvent event;
/* No-op if we aren't in a transaction. (Shouldn't happen?) */
if (deferredTriggers == NULL)
return false;
/* Scan queued events */
for (event = deferredTriggers->deftrig_events;
event != NULL;
event = event->dte_next)
{
/*
* We can ignore completed events.
*/
if (event->dte_event & (TRIGGER_DEFERRED_DONE |
TRIGGER_DEFERRED_CANCELED))
continue;
if (event->dte_relid == relid)
return true;
}
return false;
}
/* ----------
* DeferredTriggerSaveEvent()

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: tablecmds.h,v 1.13 2003/08/04 02:40:13 momjian Exp $
* $Id: tablecmds.h,v 1.13.4.1 2008/05/27 21:14:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -15,6 +15,7 @@
#define TABLECMDS_H
#include "nodes/parsenodes.h"
#include "utils/rel.h"
extern void AlterTableAddColumn(Oid myrelid, bool recurse, ColumnDef *colDef);
@ -55,6 +56,8 @@ extern Oid DefineRelation(CreateStmt *stmt, char relkind);
extern void RemoveRelation(const RangeVar *relation, DropBehavior behavior);
extern void CheckTableNotInUse(Relation rel, const char *stmt);
extern void TruncateRelation(const RangeVar *relation);
extern void renameatt(Oid myrelid,

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: trigger.h,v 1.44 2003/10/06 16:38:28 tgl Exp $
* $Id: trigger.h,v 1.44.2.1 2008/05/27 21:14:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -192,6 +192,8 @@ extern void DeferredTriggerAbortXact(void);
extern void DeferredTriggerSetState(ConstraintsSetStmt *stmt);
extern bool AfterTriggerPendingOnRel(Oid relid);
/*
* in utils/adt/ri_triggers.c