This is an automated email from the ASF dual-hosted git repository. jgemignani pushed a commit to branch PG12 in repository https://gitbox.apache.org/repos/asf/age.git
commit 100bbc19bdf5907fd631be11a5f19f3dbf02b6eb Author: John Gemignani <[email protected]> AuthorDate: Tue Oct 4 12:28:35 2022 -0700 Fix EXPLAIN to allow for nested cypher commands Fixed the EXPLAIN command to allow for nested cypher commands within more complex SQL queries. --- src/backend/parser/cypher_analyze.c | 117 ++++++++++++++++++++++++------------ 1 file changed, 77 insertions(+), 40 deletions(-) diff --git a/src/backend/parser/cypher_analyze.c b/src/backend/parser/cypher_analyze.c index ba65420..e3360cc 100644 --- a/src/backend/parser/cypher_analyze.c +++ b/src/backend/parser/cypher_analyze.c @@ -44,7 +44,17 @@ #include "utils/age_session_info.h" #include "utils/agtype.h" +/* + * extra_node is a global variable to this source to store, at the moment, the + * explain stmt node passed up by the parser. The return value from the parser + * contains an 'extra' value, hence the name. + */ static Node *extra_node = NULL; +/* + * Takes a query node and builds an explain stmt query node. It then replaces + * the passed query node with the new explain stmt query node. + */ +static void build_explain_query(Query *query, Node *explain_node); static post_parse_analyze_hook_type prev_post_parse_analyze_hook; @@ -79,9 +89,34 @@ void post_parse_analyze_fini(void) static void post_parse_analyze(ParseState *pstate, Query *query) { if (prev_post_parse_analyze_hook) + { prev_post_parse_analyze_hook(pstate, query); + } + + /* + * extra_node is set in the parsing stage to keep track of EXPLAIN. + * So it needs to be set to NULL prior to any cypher parsing. + */ + extra_node = NULL; convert_cypher_walker((Node *)query, pstate); + + /* + * If there is an extra_node returned, we need to check to see if + * it is an EXPLAIN. + */ + if (extra_node != NULL) + { + /* process the EXPLAIN node */ + if (nodeTag(extra_node) == T_ExplainStmt) + { + build_explain_query(query, extra_node); + } + + /* reset extra_node */ + pfree(extra_node); + extra_node = NULL; + } } // find cypher() calls in FROM clauses and convert them to SELECT subqueries @@ -178,57 +213,55 @@ static bool convert_cypher_walker(Node *node, ParseState *pstate) flags = QTW_EXAMINE_RTES_BEFORE | QTW_IGNORE_RT_SUBQUERIES | QTW_IGNORE_JOINALIASES; - /* clear the global variable extra_node */ - extra_node = NULL; - /* recurse on query */ result = query_tree_walker(query, convert_cypher_walker, pstate, flags); - /* check for EXPLAIN */ - if (extra_node != NULL && nodeTag(extra_node) == T_ExplainStmt) - { - ExplainStmt *estmt = NULL; - Query *query_copy = NULL; - Query *query_node = NULL; + return result; + } - /* - * Create a copy of the query node. This is purposely a shallow copy - * because we are only moving the contents to another pointer. - */ - query_copy = (Query *) palloc(sizeof(Query)); - memcpy(query_copy, query, sizeof(Query)); + return expression_tree_walker(node, convert_cypher_walker, pstate); +} - /* build our Explain node and store the query node copy in it */ - estmt = makeNode(ExplainStmt); - estmt->query = (Node *)query_copy; - estmt->options = ((ExplainStmt *)extra_node)->options; +/* + * Takes a query node and builds an explain stmt query node. It then replaces + * the passed query node with the new explain stmt query node. + */ +static void build_explain_query(Query *query, Node *explain_node) +{ + ExplainStmt *estmt = NULL; + Query *query_copy = NULL; + Query *query_node = NULL; - /* build our replacement query node */ - query_node = makeNode(Query); - query_node->commandType = CMD_UTILITY; - query_node->utilityStmt = (Node *)estmt; - query_node->canSetTag = true; + /* + * Create a copy of the query node. This is purposely a shallow copy + * because we are only moving the contents to another pointer. + */ + query_copy = (Query *) palloc(sizeof(Query)); + memcpy(query_copy, query, sizeof(Query)); - /* now replace the top query node with our replacement query node */ - memcpy(query, query_node, sizeof(Query)); + /* build our Explain node and store the query node copy in it */ + estmt = makeNode(ExplainStmt); + estmt->query = (Node *)query_copy; + estmt->options = ((ExplainStmt *)explain_node)->options; - /* - * We need to free and clear the global variable when done. But, not - * the ExplainStmt options. Those will get freed by PG when the - * query is deleted. - */ - ((ExplainStmt *)extra_node)->options = NULL; - pfree(extra_node); - extra_node = NULL; + /* build our replacement query node */ + query_node = makeNode(Query); + query_node->commandType = CMD_UTILITY; + query_node->utilityStmt = (Node *)estmt; + query_node->canSetTag = true; - /* we need to free query_node as it is no longer needed */ - pfree(query_node); - } + /* now replace the top query node with our replacement query node */ + memcpy(query, query_node, sizeof(Query)); - return result; - } + /* + * We need to free and clear the global variable when done. But, not + * the ExplainStmt options. Those will get freed by PG when the + * query is deleted. + */ + ((ExplainStmt *)explain_node)->options = NULL; - return expression_tree_walker(node, convert_cypher_walker, pstate); + /* we need to free query_node as it is no longer needed */ + pfree(query_node); } static bool is_rte_cypher(RangeTblEntry *rte) @@ -475,6 +508,10 @@ static void convert_cypher_to_subquery(RangeTblEntry *rte, ParseState *pstate) { Node *temp = llast(stmt); + ereport(WARNING, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("too many extra_nodes passed from parser"))); + list_delete_ptr(stmt, temp); }
