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));