summaryrefslogtreecommitdiff
path: root/src/backend/commands/trigger.c
diff options
context:
space:
mode:
authorTom Lane2008-01-02 23:34:42 +0000
committerTom Lane2008-01-02 23:34:42 +0000
commit20e862155f1000804f9e2ae20b7fbb9a55623951 (patch)
treeaa73bd2379ab9fa89abc9e3ba73e723934fc8b06 /src/backend/commands/trigger.c
parent69528aefbbf2c1bfb68b488fafd08bea390eb3f5 (diff)
Forbid ALTER TABLE and CLUSTER when there are pending AFTER-trigger events
in the current backend for the target table. These operations move tuples around and would thus invalidate the TIDs stored in the trigger event records. (We need not worry about events in other backends, since acquiring exclusive lock should be enough to ensure there aren't any.) It might be sufficient to forbid only the table-rewriting variants of ALTER TABLE, but in the absence of any compelling use-case, let's just be safe and simple. Per follow-on investigation of bug #3847, though this is not actually the same problem reported therein. Possibly this should be back-patched, but since the case has never been reported from the field, I didn't bother.
Diffstat (limited to 'src/backend/commands/trigger.c')
-rw-r--r--src/backend/commands/trigger.c43
1 files changed, 20 insertions, 23 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 7fca0cd5af0..5bca2233279 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
- * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.226 2008/01/01 19:45:49 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.227 2008/01/02 23:34:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -3478,28 +3478,29 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
}
/* ----------
- * AfterTriggerCheckTruncate()
- * Test deferred-trigger status to see if a TRUNCATE is OK.
+ * AfterTriggerPendingOnRel()
+ * Test to see if there are any pending after-trigger events for rel.
*
- * The argument is a list of OIDs of relations due to be truncated.
- * We raise error if there are any pending after-trigger events for them.
+ * 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.
* ----------
*/
-void
-AfterTriggerCheckTruncate(List *relids)
+bool
+AfterTriggerPendingOnRel(Oid relid)
{
AfterTriggerEvent event;
int depth;
- /*
- * Ignore call if we aren't in a transaction. (Shouldn't happen?)
- */
+ /* No-op if we aren't in a transaction. (Shouldn't happen?) */
if (afterTriggers == NULL)
- return;
+ return false;
/* Scan queued events */
for (event = afterTriggers->events.head;
@@ -3509,21 +3510,18 @@ AfterTriggerCheckTruncate(List *relids)
/*
* We can ignore completed events. (Even if a DONE flag is rolled
* back by subxact abort, it's OK because the effects of the TRUNCATE
- * must get rolled back too.)
+ * or whatever must get rolled back too.)
*/
if (event->ate_event & AFTER_TRIGGER_DONE)
continue;
- if (list_member_oid(relids, event->ate_relid))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot truncate table \"%s\" because it has pending trigger events",
- get_rel_name(event->ate_relid))));
+ if (event->ate_relid == relid)
+ return true;
}
/*
* Also scan events queued by incomplete queries. This could only matter
- * if a TRUNCATE is executed by a function or trigger within an updating
+ * if TRUNCATE/etc is executed by a function or trigger within an updating
* query on the same relation, which is pretty perverse, but let's check.
*/
for (depth = 0; depth <= afterTriggers->query_depth; depth++)
@@ -3535,13 +3533,12 @@ AfterTriggerCheckTruncate(List *relids)
if (event->ate_event & AFTER_TRIGGER_DONE)
continue;
- if (list_member_oid(relids, event->ate_relid))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot truncate table \"%s\" because it has pending trigger events",
- get_rel_name(event->ate_relid))));
+ if (event->ate_relid == relid)
+ return true;
}
}
+
+ return false;
}