Fix mishandling of after-trigger state when a SQL function returns multiple
authorTom Lane
Thu, 12 Oct 2006 17:02:24 +0000 (17:02 +0000)
committerTom Lane
Thu, 12 Oct 2006 17:02:24 +0000 (17:02 +0000)
rows --- if the surrounding query queued any trigger events between the rows,
the events would be fired at the wrong time, leading to bizarre behavior.
Per report from Merlin Moncure.

This is a simple patch that should solve the problem fully in the back
branches, but in HEAD we also need to consider the possibility of queries
with RETURNING clauses.  Will look into a fix for that separately.

src/backend/executor/functions.c

index f9b8067109bb75f5688cc002a15477a93fc34c49..278960b4970a95a48b98cf54de05e5a3fc594f6d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.107 2006/10/04 00:29:52 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.108 2006/10/12 17:02:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -327,7 +327,14 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
    /* Utility commands don't need Executor. */
    if (es->qd->operation != CMD_UTILITY)
    {
-       AfterTriggerBeginQuery();
+       /*
+        * Only set up to collect queued triggers if it's not a SELECT.
+        * This isn't just an optimization, but is necessary in case a SELECT
+        * returns multiple rows to caller --- we mustn't exit from the
+        * function execution with a stacked AfterTrigger level still active.
+        */
+       if (es->qd->operation != CMD_SELECT)
+           AfterTriggerBeginQuery();
        ExecutorStart(es->qd, 0);
    }
 
@@ -401,7 +408,8 @@ postquel_end(execution_state *es)
        {
            ActiveSnapshot = es->qd->snapshot;
 
-           AfterTriggerEndQuery(es->qd->estate);
+           if (es->qd->operation != CMD_SELECT)
+               AfterTriggerEndQuery(es->qd->estate);
            ExecutorEnd(es->qd);
        }
        PG_CATCH();