Fix plancache refcount leak after error in ExecuteQuery.
authorTom Lane
Wed, 16 Jun 2021 23:30:17 +0000 (19:30 -0400)
committerTom Lane
Wed, 16 Jun 2021 23:30:17 +0000 (19:30 -0400)
When stuffing a plan from the plancache into a Portal, one is
not supposed to risk throwing an error between GetCachedPlan and
PortalDefineQuery; if that happens, the plan refcount incremented
by GetCachedPlan will be leaked.  I managed to break this rule
while refactoring code in 9dbf2b7d7.  There is no visible
consequence other than some memory leakage, and since nobody is
very likely to trigger the relevant error conditions many times
in a row, it's not surprising we haven't noticed.  Nonetheless,
it's a bug, so rearrange the order of operations to remove the
hazard.

Noted on the way to looking for a better fix for bug #17053.
This mistake is pretty old, so back-patch to all supported
branches.

src/backend/commands/prepare.c

index 80d6df8ac1e78e44adda1edb60ec99c728b9a994..e54cfc8ad88ec0c6fa8d8d393cc3fde28f91f761 100644 (file)
@@ -233,6 +233,17 @@ ExecuteQuery(ParseState *pstate,
    cplan = GetCachedPlan(entry->plansource, paramLI, false, NULL);
    plan_list = cplan->stmt_list;
 
+   /*
+    * DO NOT add any logic that could possibly throw an error between
+    * GetCachedPlan and PortalDefineQuery, or you'll leak the plan refcount.
+    */
+   PortalDefineQuery(portal,
+                     NULL,
+                     query_string,
+                     entry->plansource->commandTag,
+                     plan_list,
+                     cplan);
+
    /*
     * For CREATE TABLE ... AS EXECUTE, we must verify that the prepared
     * statement is one that produces tuples.  Currently we insist that it be
@@ -276,13 +287,6 @@ ExecuteQuery(ParseState *pstate,
        count = FETCH_ALL;
    }
 
-   PortalDefineQuery(portal,
-                     NULL,
-                     query_string,
-                     entry->plansource->commandTag,
-                     plan_list,
-                     cplan);
-
    /*
     * Run the portal as appropriate.
     */