From bde689f809027422d4c0baaa3e63b69ce5762e2c Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Wed, 27 Jun 2012 23:18:30 -0400 Subject: [PATCH] Make UtilityContainsQuery recurse until it finds a non-utility Query. The callers of UtilityContainsQuery want it to return a non-utility Query if it returns anything at all. However, since we made CREATE TABLE AS/SELECT INTO into a utility command instead of a variant of SELECT, a command like "EXPLAIN SELECT INTO" results in two nested utility statements. So what we need UtilityContainsQuery to do is drill down to the bottom non-utility Query. I had thought of this possibility in setrefs.c, and fixed it there by looping around the UtilityContainsQuery call; but overlooked that the call sites in plancache.c have a similar issue. In those cases it's notationally inconvenient to provide an external loop, so let's redefine UtilityContainsQuery as recursing down to a non-utility Query instead. Noted by Rushabh Lathia. This is a somewhat cleaned-up version of his proposed patch. --- src/backend/optimizer/plan/setrefs.c | 2 +- src/backend/tcop/utility.c | 26 ++++++++++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index f375b5f76d4..ccd69fc3660 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -1937,7 +1937,7 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context) Query *query = (Query *) node; ListCell *lc; - while (query->commandType == CMD_UTILITY) + if (query->commandType == CMD_UTILITY) { /* * Ignore utility statements, except those (such as EXPLAIN) that diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 8b73858300e..33b292e6b7a 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -1351,25 +1351,39 @@ QueryReturnsTuples(Query *parsetree) * UtilityContainsQuery * Return the contained Query, or NULL if there is none * - * Certain utility statements, such as EXPLAIN, contain a Query. + * Certain utility statements, such as EXPLAIN, contain a plannable Query. * This function encapsulates knowledge of exactly which ones do. * We assume it is invoked only on already-parse-analyzed statements * (else the contained parsetree isn't a Query yet). + * + * In some cases (currently, only EXPLAIN of CREATE TABLE AS/SELECT INTO), + * potentially Query-containing utility statements can be nested. This + * function will drill down to a non-utility Query, or return NULL if none. */ Query * UtilityContainsQuery(Node *parsetree) { + Query *qry; + switch (nodeTag(parsetree)) { case T_ExplainStmt: - Assert(IsA(((ExplainStmt *) parsetree)->query, Query)); - return (Query *) ((ExplainStmt *) parsetree)->query; + qry = (Query *) ((ExplainStmt *) parsetree)->query; + Assert(IsA(qry, Query)); + if (qry->commandType == CMD_UTILITY) + return UtilityContainsQuery(qry->utilityStmt); + return qry; case T_CreateTableAsStmt: /* might or might not contain a Query ... */ - if (IsA(((CreateTableAsStmt *) parsetree)->query, Query)) - return (Query *) ((CreateTableAsStmt *) parsetree)->query; - Assert(IsA(((CreateTableAsStmt *) parsetree)->query, ExecuteStmt)); + qry = (Query *) ((CreateTableAsStmt *) parsetree)->query; + if (IsA(qry, Query)) + { + /* Recursion currently can't be necessary here */ + Assert(qry->commandType != CMD_UTILITY); + return qry; + } + Assert(IsA(qry, ExecuteStmt)); return NULL; default: -- GitLab