I wrote:
> The attached quick draft (needs attention to comments) results in
> regression=# \parse s
> regression=# execute s;
> EXECUTE
> which matches the behavior of pre-v12 servers.
Another way that's perhaps a bit less magical is to make
ExecuteQuery substitute the right thing, as attached.
I'm not entirely sure whether this leaves any other code
paths that can reach that Assert; but on the other hand,
if there are any, CMDTAG_EXECUTE is probably wrong for them.
BTW, the reason why EXPLAIN EXECUTE isn't at risk is that it's
going to return CMDTAG_EXPLAIN.
regards, tom lane
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 34b6410d6a2..6ecc24fdba8 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -159,6 +159,7 @@ ExecuteQuery(ParseState *pstate,
EState *estate = NULL;
Portal portal;
char *query_string;
+ CommandTag commandTag;
int eflags;
long count;
@@ -196,6 +197,15 @@ ExecuteQuery(ParseState *pstate,
cplan = GetCachedPlan(entry->plansource, paramLI, NULL, NULL);
plan_list = cplan->stmt_list;
+ /*
+ * Normally the PlanSource will have a usable commandTag, but if it has an
+ * empty statement list, then not so much. Substitute EXECUTE to avoid
+ * failing because we have no commandTag to report to the client.
+ */
+ commandTag = entry->plansource->commandTag;
+ if (commandTag == CMDTAG_UNKNOWN)
+ commandTag = CMDTAG_EXECUTE;
+
/*
* DO NOT add any logic that could possibly throw an error between
* GetCachedPlan and PortalDefineQuery, or you'll leak the plan refcount.
@@ -203,7 +213,7 @@ ExecuteQuery(ParseState *pstate,
PortalDefineQuery(portal,
NULL,
query_string,
- entry->plansource->commandTag,
+ commandTag,
plan_list,
cplan);