diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 3d782d2e5f4..041fdfb70bd 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -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); /* diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 3256b172206..19ea0cac1ae 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -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); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 97caafbb8e1..ccc25c88f4a 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -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 diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 4b9d5efea98..9d2b2b64e98 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -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() diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h index 793f3a6c799..4fb15de071c 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -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, diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h index fe8f6af61d9..9a15bbdf04a 100644 --- a/src/include/commands/trigger.h +++ b/src/include/commands/trigger.h @@ -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