OSDN Git Service

Disallow TRUNCATE when there are any pending after-trigger events for
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 4 Sep 2006 21:15:56 +0000 (21:15 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 4 Sep 2006 21:15:56 +0000 (21:15 +0000)
the target relation(s).  There might be some cases where we could discard
the pending event instead, but for the moment a conservative approach
seems sufficient.  Per report from Markus Schiltknecht and subsequent
discussion.

src/backend/commands/tablecmds.c
src/backend/commands/trigger.c
src/include/commands/trigger.h

index 7e88844..45167b8 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.201 2006/08/25 04:06:48 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.202 2006/09/04 21:15:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -612,6 +612,13 @@ ExecuteTruncate(TruncateStmt *stmt)
 #endif
 
        /*
+        * Also check for pending AFTER trigger events on the target relations.
+        * We can't just leave those be, since they will try to fetch tuples
+        * that the TRUNCATE removes.
+        */
+       AfterTriggerCheckTruncate(relids);
+
+       /*
         * OK, truncate each table.
         */
        foreach(cell, rels)
index 42d89a9..23dc503 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.206 2006/08/03 16:04:41 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.207 2006/09/04 21:15:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -3117,6 +3117,74 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
        }
 }
 
+/* ----------
+ * AfterTriggerCheckTruncate()
+ *             Test deferred-trigger status to see if a TRUNCATE is OK.
+ *
+ * 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.
+ *
+ * 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)
+{
+       AfterTriggerEvent event;
+       int                     depth;
+
+       /*
+        * Ignore call if we aren't in a transaction.  (Shouldn't happen?)
+        */
+       if (afterTriggers == NULL)
+               return;
+
+       /* Scan queued events */
+       for (event = afterTriggers->events.head;
+                event != NULL;
+                event = event->ate_next)
+       {
+               /*
+                * 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.)
+                */
+               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))));
+       }
+
+       /*
+        * Also scan events queued by incomplete queries.  This could only
+        * matter if a TRUNCATE 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++)
+       {
+               for (event = afterTriggers->query_stack[depth].head;
+                        event != NULL;
+                        event = event->ate_next)
+               {
+                       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))));
+               }
+       }
+}
+
 
 /* ----------
  * AfterTriggerSaveEvent()
index 0cb4df7..31253d8 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.58 2006/06/16 20:23:45 adunstan Exp $
+ * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.59 2006/09/04 21:15:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -164,8 +164,8 @@ extern void AfterTriggerFireDeferred(void);
 extern void AfterTriggerEndXact(bool isCommit);
 extern void AfterTriggerBeginSubXact(void);
 extern void AfterTriggerEndSubXact(bool isCommit);
-
 extern void AfterTriggerSetState(ConstraintsSetStmt *stmt);
+extern void AfterTriggerCheckTruncate(List *relids);
 
 
 /*