You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@age.apache.org by jg...@apache.org on 2022/10/06 00:59:52 UTC

[age] branch master updated: Fix EXPLAIN to allow for nested cypher commands

This is an automated email from the ASF dual-hosted git repository.

jgemignani pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/age.git


The following commit(s) were added to refs/heads/master by this push:
     new c9c07f7  Fix EXPLAIN to allow for nested cypher commands
c9c07f7 is described below

commit c9c07f71123d2f11d6209188633dff1ad341d23a
Author: John Gemignani <jr...@gmail.com>
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 15222ff..1a8ea23 100644
--- a/src/backend/parser/cypher_analyze.c
+++ b/src/backend/parser/cypher_analyze.c
@@ -44,7 +44,17 @@
 #include "utils/ag_func.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 | 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)
@@ -386,6 +419,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);
     }