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/05/05 00:35:52 UTC

[incubator-age] branch master updated: VLE path variable integration performance patch

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/incubator-age.git


The following commit(s) were added to refs/heads/master by this push:
     new ad5bd9f  VLE path variable integration performance patch
ad5bd9f is described below

commit ad5bd9f9cc434a7694ffd8e83ce785f1fd7eba52
Author: John Gemignani <jr...@gmail.com>
AuthorDate: Fri Apr 22 17:44:11 2022 -0700

    VLE path variable integration performance patch
    
    This patch fixes 2 cases where the match terminal edge logic
    for the VLE integration into a match shouldn't be called for a path
    variable. The cases are as follows -
    
        1) p=()-[*]-(), p=()-[*]->(), p=()<-[*]-()
        2) p=(u)-[*]-(), p=(u)-[*]->(), p=(u)<-[*]-()
    
    Note: This is for any length of VLE edge, not just *
    
    Note: It may be possible to expand this to other cases. This is left
          for a potential future patch.
    
    In the cases listed above, this decreases the execution speed by
    orders of magnitude.
    
    There are no impact on regression tests.
---
 src/backend/parser/cypher_clause.c |  57 +++++--
 src/backend/parser/cypher_gram.y   | 315 +++++++++++++++++++------------------
 src/backend/utils/adt/agtype.c     |  48 +++---
 3 files changed, 241 insertions(+), 179 deletions(-)

diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c
index 67dea9e..a59c6d3 100644
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@ -381,6 +381,7 @@ static RangeTblEntry *append_VLE_Func_to_FromClause(cypher_parsestate *cpstate,
                                                     Node *n);
 static void setNamespaceLateralState(List *namespace, bool lateral_only,
                                      bool lateral_ok);
+static bool isa_special_VLE_case(cypher_path *path);
 
 /*
  * transform a cypher_clause
@@ -3420,6 +3421,31 @@ static transform_entity *transform_VLE_edge_entity(cypher_parsestate *cpstate,
     return vle_entity;
 }
 
+/* helper function to check for specific VLE cases */
+static bool isa_special_VLE_case(cypher_path *path)
+{
+    cypher_relationship *cr = NULL;
+
+    if (path->var_name == NULL)
+    {
+        return false;
+    }
+
+    if (list_length(path->path) != 3)
+    {
+        return false;
+    }
+
+    cr = (cypher_relationship*)lfirst(lnext(list_head(path->path)));
+
+    if (cr->varlen != NULL)
+    {
+        return true;
+    }
+
+    return false;
+}
+
 /*
  * Iterate through the path and construct all edges and necessary vertices
  */
@@ -3432,6 +3458,9 @@ static List *transform_match_entities(cypher_parsestate *cpstate, Query *query,
     int i = 0;
     bool node_declared_in_prev_clause = false;
     transform_entity *prev_entity = NULL;
+    bool special_VLE_case = false;
+
+    special_VLE_case = isa_special_VLE_case(path);
 
     /*
      * Iterate through every node in the path, construct the expr node
@@ -3448,6 +3477,7 @@ static List *transform_match_entities(cypher_parsestate *cpstate, Query *query,
         if (i % 2 == 0)
         {
             cypher_node *node = NULL;
+            bool output_node = false;
 
             node = lfirst(lc);
 
@@ -3471,9 +3501,14 @@ static List *transform_match_entities(cypher_parsestate *cpstate, Query *query,
                 }
             }
 
+            /* should we make the node available */
+            output_node = (special_VLE_case && !node->name && !node->props) ?
+                          false :
+                          INCLUDE_NODE_IN_JOIN_TREE(path, node);
+
             /* transform vertex */
             expr = transform_cypher_node(cpstate, node, &query->targetList,
-                                         INCLUDE_NODE_IN_JOIN_TREE(path, node));
+                                         output_node);
 
             entity = make_transform_entity(cpstate, ENT_VERTEX, (Node *)node,
                                            expr);
@@ -3641,8 +3676,8 @@ static List *make_path_join_quals(cypher_parsestate *cpstate, List *entities)
         }
 
         // create the join quals for the node
-        join_quals = make_join_condition_for_edge(
-            cpstate, prev_edge, prev_node, edge, next_node, next_edge);
+        join_quals = make_join_condition_for_edge(cpstate, prev_edge, prev_node,
+                                                  edge, next_node, next_edge);
 
         quals = list_concat(quals, join_quals);
 
@@ -3691,7 +3726,10 @@ transform_match_create_path_variable(cypher_parsestate *cpstate,
     {
         transform_entity *entity = lfirst(lc);
 
-        entity_exprs = lappend(entity_exprs, entity->expr);
+        if (entity->expr != NULL)
+        {
+            entity_exprs = lappend(entity_exprs, entity->expr);
+        }
     }
 
     // get the oid for the path creation function
@@ -4031,8 +4069,7 @@ static Expr *transform_cypher_node(cypher_parsestate *cpstate,
                      parser_errposition(pstate, node->location)));
         }
     }
-
-    if (!node->name)
+    else
     {
         node->name = get_next_default_alias(cpstate);
     }
@@ -4054,11 +4091,9 @@ static Expr *transform_cypher_node(cypher_parsestate *cpstate,
 
     expr = (Expr *)make_vertex_expr(cpstate, rte, node->label);
 
-    if (node->name)
-    {
-        te = makeTargetEntry(expr, resno, node->name, false);
-        *target_list = lappend(*target_list, te);
-    }
+    /* make target entry and add it */
+    te = makeTargetEntry(expr, resno, node->name, false);
+    *target_list = lappend(*target_list, te);
 
     return expr;
 }
diff --git a/src/backend/parser/cypher_gram.y b/src/backend/parser/cypher_gram.y
index bd86911..b751cdb 100644
--- a/src/backend/parser/cypher_gram.y
+++ b/src/backend/parser/cypher_gram.y
@@ -218,6 +218,12 @@ static Node *make_function_expr(List *func_name, List *exprs, int location);
 static Node *make_set_op(SetOperation op, bool all_or_distinct, List *larg,
                          List *rarg);
 
+// VLE
+static cypher_relationship *build_VLE_relation(List *left_arg,
+                                               cypher_relationship *cr,
+                                               Node *right_arg,
+                                               int left_arg_location,
+                                               int cr_location);
 // comparison
 static bool is_A_Expr_a_comparison_operation(A_Expr *a);
 static Node *build_comparison_expression(Node *left_grammar_node,
@@ -1046,159 +1052,13 @@ simple_path:
             /* if this is a VLE relation node */
             if (cr->varlen != NULL)
             {
-                ColumnRef *cref = NULL;
-                A_Indices *ai = NULL;
-                List *args = NIL;
-                List *eargs = NIL;
-                List *fname = NIL;
-                cypher_node *cnl = NULL;
-                cypher_node *cnr = NULL;
-                Node *node = NULL;
-                int length = 0;
-                unsigned long unique_number = 0;
-                int location = 0;
-
-                /* get a unique number to identify this VLE node */
-                unique_number = get_a_unique_number();
-
-                /* get the location */
-                location = cr->location;
-
-                /* get the left and right cypher_nodes */
-                cnl = (cypher_node*)llast($1);
-                cnr = (cypher_node*)$3;
-
-                /* get the length of the left path */
-                length = list_length($1);
+                /* build the VLE relation */
+                cr = build_VLE_relation($1, cr, $3, @1, @2);
 
-                /*
-                 * If the left name is NULL and the left path is greater than 1
-                 * If the left name is NULL and the left label is not NULL
-                 * If the left name is NULL and the left props is not NULL
-                 * If the left name is NULL and the right name is not NULL
-                 * If the left name is NULL and the right label is not NULL
-                 * If the left name is NULL and the right props is not NULL
-                 * we need to create a variable name for the left node.
-                 */
-                if ((cnl->name == NULL && length > 1) ||
-                    (cnl->name == NULL && cnl->label != NULL) ||
-                    (cnl->name == NULL && cnl->props != NULL) ||
-                    (cnl->name == NULL && cnr->name != NULL) ||
-                    (cnl->name == NULL && cnr->label != NULL) ||
-                    (cnl->name == NULL && cnr->props != NULL))
-                {
-                    cnl->name = create_unique_name("_vle_function_start_var");
-                }
-                /* add in the start vertex as a ColumnRef if necessary */
-                if (cnl->name != NULL)
-                {
-                    cref = makeNode(ColumnRef);
-                    cref->fields = list_make2(makeString(cnl->name),
-                                              makeString("id"));
-                    cref->location = @1;
-                    args = lappend(args, cref);
-                }
-                /*
-                 * If there aren't any variables in the VLE path, we can use
-                 * the FROM_ALL algorithm.
-                 */
-                else
-                {
-                    args = lappend(args, make_null_const(-1));
-                }
-
-                /*
-                 * Create a variable name for the end vertex if we have a label
-                 * name or props but we don't have a variable name.
-                 *
-                 * For example: ()-[*]-(:label) or ()-[*]-({name: "John"})
-                 *
-                 * We need this so the addition of match_vle_terminal_edge is
-                 * done in the transform phase.
-                 */
-                if (cnr->name == NULL &&
-                    (cnr->label != NULL || cnr->props != NULL))
-                {
-                    cnr->name = create_unique_name("_vle_function_end_var");
-                }
-                /*
-                 * We need a NULL for the target vertex in the VLE match to
-                 * force the dfs_find_a_path_from algorithm. However, for now,
-                 * the default will be to only do that when a target isn't
-                 * supplied.
-                 *
-                 * TODO: We will likely want to force it to use
-                 * dfs_find_a_path_from.
-                 */
-                if (cnl->name == NULL && cnr->name != NULL)
-                {
-                    cref = makeNode(ColumnRef);
-                    cref->fields = list_make2(makeString(cnr->name),
-                                              makeString("id"));
-                    cref->location = @1;
-                    args = lappend(args, cref);
-                }
-                else
-                {
-                    args = lappend(args, make_null_const(-1));
-                }
-
-                /* build the required edge arguments */
-                if (cr->label == NULL)
-                {
-                    eargs = lappend(eargs, make_null_const(location));
-                }
-                else
-                {
-                    eargs = lappend(eargs, make_string_const(cr->label,
-                                                             location));
-                }
-                if (cr->props == NULL)
-                {
-                    eargs = lappend(eargs, make_null_const(location));
-                }
-                else
-                {
-                    eargs = lappend(eargs, cr->props);
-                }
-                /* build the edge function name (schema.funcname) */
-                fname = list_make2(makeString("ag_catalog"),
-                                   makeString("age_build_vle_match_edge"));
-                /* build the edge function node */
-                node = make_function_expr(fname, eargs, location);
-                /* add in the edge*/
-                args = lappend(args, node);
-
-                /* add in the lidx and uidx range as Const */
-                ai = (A_Indices*)cr->varlen;
-                if (ai == NULL || ai->lidx == NULL)
-                {
-                    args = lappend(args, make_null_const(location));
-                }
-                else
-                {
-                    args = lappend(args, ai->lidx);
-                }
-                if (ai == NULL || ai->uidx == NULL)
-                {
-                    args = lappend(args, make_null_const(location));
-                }
-                else
-                {
-                    args = lappend(args, ai->uidx);
-                }
-                /* add in the direction as Const */
-                args = lappend(args, make_int_const(cr->dir, @2));
-
-                /* add in the unique number used to identify this VLE node */
-                args = lappend(args, make_int_const(unique_number, -1));
-
-                /* build the VLE function node */
-                cr->varlen = make_function_expr(list_make1(makeString("vle")),
-                                                args, @2);
                 /* return the VLE relation in the path */
                 $$ = lappend(lappend($1, cr), $3);
             }
+            /* otherwise, it is a regular relationship node */
             else
             {
                 $$ = lappend(lappend($1, $2), $3);
@@ -2405,3 +2265,160 @@ static Node *build_comparison_expression(Node *left_grammar_node,
 
     return result_expr;
 }
+
+static cypher_relationship *build_VLE_relation(List *left_arg,
+                                               cypher_relationship *cr,
+                                               Node *right_arg,
+                                               int left_arg_location,
+                                               int cr_location)
+{
+    ColumnRef *cref = NULL;
+    A_Indices *ai = NULL;
+    List *args = NIL;
+    List *eargs = NIL;
+    List *fname = NIL;
+    cypher_node *cnl = NULL;
+    cypher_node *cnr = NULL;
+    Node *node = NULL;
+    int length = 0;
+    unsigned long unique_number = 0;
+    int location = 0;
+
+    /* get a unique number to identify this VLE node */
+    unique_number = get_a_unique_number();
+
+    /* get the location */
+    location = cr->location;
+
+    /* get the left and right cypher_nodes */
+    cnl = (cypher_node*)llast(left_arg);
+    cnr = (cypher_node*)right_arg;
+
+    /* get the length of the left path */
+    length = list_length(left_arg);
+
+    /*
+     * If the left name is NULL and the left path is greater than 1
+     * If the left name is NULL and the left label is not NULL
+     * If the left name is NULL and the left props is not NULL
+     * If the left name is NULL and the right name is not NULL
+     * If the left name is NULL and the right label is not NULL
+     * If the left name is NULL and the right props is not NULL
+     * we need to create a variable name for the left node.
+     */
+    if ((cnl->name == NULL && length > 1) ||
+        (cnl->name == NULL && cnl->label != NULL) ||
+        (cnl->name == NULL && cnl->props != NULL) ||
+        (cnl->name == NULL && cnr->name != NULL) ||
+        (cnl->name == NULL && cnr->label != NULL) ||
+        (cnl->name == NULL && cnr->props != NULL))
+    {
+        cnl->name = create_unique_name("_vle_function_start_var");
+    }
+
+    /* add in the start vertex as a ColumnRef if necessary */
+    if (cnl->name != NULL)
+    {
+        cref = makeNode(ColumnRef);
+        cref->fields = list_make2(makeString(cnl->name), makeString("id"));
+        cref->location = left_arg_location;
+        args = lappend(args, cref);
+    }
+    /*
+     * If there aren't any variables in the VLE path, we can use
+     * the FROM_ALL algorithm.
+     */
+    else
+    {
+        args = lappend(args, make_null_const(-1));
+    }
+
+    /*
+     * Create a variable name for the end vertex if we have a label
+     * name or props but we don't have a variable name.
+     *
+     * For example: ()-[*]-(:label) or ()-[*]-({name: "John"})
+     *
+     * We need this so the addition of match_vle_terminal_edge is
+     * done in the transform phase.
+     */
+    if (cnr->name == NULL &&
+        (cnr->label != NULL || cnr->props != NULL))
+    {
+        cnr->name = create_unique_name("_vle_function_end_var");
+    }
+    /*
+     * We need a NULL for the target vertex in the VLE match to
+     * force the dfs_find_a_path_from algorithm. However, for now,
+     * the default will be to only do that when a target isn't
+     * supplied.
+     *
+     * TODO: We will likely want to force it to use
+     * dfs_find_a_path_from.
+     */
+    if (cnl->name == NULL && cnr->name != NULL)
+    {
+        cref = makeNode(ColumnRef);
+        cref->fields = list_make2(makeString(cnr->name), makeString("id"));
+        cref->location = left_arg_location;
+        args = lappend(args, cref);
+    }
+    else
+    {
+        args = lappend(args, make_null_const(-1));
+    }
+
+    /* build the required edge arguments */
+    if (cr->label == NULL)
+    {
+        eargs = lappend(eargs, make_null_const(location));
+    }
+    else
+    {
+        eargs = lappend(eargs, make_string_const(cr->label, location));
+    }
+    if (cr->props == NULL)
+    {
+        eargs = lappend(eargs, make_null_const(location));
+    }
+    else
+    {
+        eargs = lappend(eargs, cr->props);
+    }
+    /* build the edge function name (schema.funcname) */
+    fname = list_make2(makeString("ag_catalog"),
+                       makeString("age_build_vle_match_edge"));
+    /* build the edge function node */
+    node = make_function_expr(fname, eargs, location);
+    /* add in the edge*/
+    args = lappend(args, node);
+    /* add in the lidx and uidx range as Const */
+    ai = (A_Indices*)cr->varlen;
+    if (ai == NULL || ai->lidx == NULL)
+    {
+        args = lappend(args, make_null_const(location));
+    }
+    else
+    {
+        args = lappend(args, ai->lidx);
+    }
+    if (ai == NULL || ai->uidx == NULL)
+    {
+        args = lappend(args, make_null_const(location));
+    }
+    else
+    {
+        args = lappend(args, ai->uidx);
+    }
+    /* add in the direction as Const */
+    args = lappend(args, make_int_const(cr->dir, cr_location));
+
+    /* add in the unique number used to identify this VLE node */
+    args = lappend(args, make_int_const(unique_number, -1));
+
+    /* build the VLE function node */
+    cr->varlen = make_function_expr(list_make1(makeString("vle")), args,
+                                    cr_location);
+    /* return the VLE relation node */
+    return cr;
+}
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index 0d38d9e..a44d250 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -1832,33 +1832,43 @@ Datum _agtype_build_path(PG_FUNCTION_ARGS)
                  errmsg("paths require at least 1 vertex")));
     }
 
-    if (nargs % 2 == 0)
-    {
-        ereport(ERROR,
-                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                 errmsg("a path is of the form: [vertex, (edge, vertex)*i] where i >= 0")));
-    }
-
     /*
-     * If this path is only 3 elements long (vertex, edge, vertex). Check to see
-     * if the edge is actually a path (made by the VLE). If so, just materialize
-     * the vle path because it already contains the two outside vertices.
+     * If this path is only 1 to 3 elements in length, check to see if the
+     * contained edge is actually a path (made by the VLE). If so, just
+     * materialize the vle path because it already contains the two outside
+     * vertices.
      */
-    if (nargs == 3)
+    if (nargs >= 1 && nargs <= 3)
     {
-        agtype *agt = NULL;
-
-        agt = DATUM_GET_AGTYPE_P(args[1]);
+        int i = 0;
 
-        if (types[1] == AGTYPEOID &&
-            AGT_ROOT_IS_BINARY(agt) &&
-            AGT_ROOT_BINARY_FLAGS(agt) == AGT_FBINARY_TYPE_VLE_PATH)
+        for (i = 0; i < nargs; i++)
         {
-            agtype *path = agt_materialize_vle_path(agt);
-            PG_RETURN_POINTER(path);
+            agtype *agt = NULL;
+
+            if (nulls[i] || types[i] != AGTYPEOID)
+            {
+                break;
+            }
+
+            agt = DATUM_GET_AGTYPE_P(args[i]);
+
+            if (AGT_ROOT_IS_BINARY(agt) &&
+                AGT_ROOT_BINARY_FLAGS(agt) == AGT_FBINARY_TYPE_VLE_PATH)
+            {
+                agtype *path = agt_materialize_vle_path(agt);
+                PG_RETURN_POINTER(path);
+            }
         }
     }
 
+    if (nargs % 2 == 0)
+    {
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("a path is of the form: [vertex, (edge, vertex)*i] where i >= 0")));
+    }
+
     /* initialize the result */
     memset(&result, 0, sizeof(agtype_in_state));