aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/catalog/index.c19
-rw-r--r--src/backend/commands/cluster.c8
-rw-r--r--src/backend/commands/tablecmds.c50
-rw-r--r--src/backend/commands/trigger.c45
-rw-r--r--src/include/commands/tablecmds.h5
-rw-r--r--src/include/commands/trigger.h4
6 files changed, 125 insertions, 6 deletions
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 $
*
*-------------------------------------------------------------------------
*/
@@ -398,6 +398,12 @@ TruncateRelation(const RangeVar *relation)
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
*/
heap_truncate_check_FKs(rel);
@@ -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