From ac32d767f8501ec24d3938417f7ef659a15c8a00 Mon Sep 17 00:00:00 2001 From: Junwang Zhao Date: Sat, 9 May 2026 10:41:40 +0800 Subject: [PATCH v1] Prune non-matching graph path prefixes during DFS Add an early feasibility check in generate_queries_for_path_pattern_recurse() so DFS stops exploring a path prefix as soon as the newly appended element can no longer satisfy edge-vertex adjacency. When the new element is an edge, validate it against any already- selected elements in the current prefix. When the new element is a vertex, validate only the immediately preceding edge. That is sufficient here because repeated vertex variables are merged into a single path factor before DFS begins. This keeps the existing query generation semantics unchanged while avoiding the work of enumerating many full-length paths that would later be rejected by generate_query_for_graph_path(). --- src/backend/rewrite/rewriteGraphTable.c | 91 ++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/src/backend/rewrite/rewriteGraphTable.c b/src/backend/rewrite/rewriteGraphTable.c index 33d4e866d74..9240fadce32 100644 --- a/src/backend/rewrite/rewriteGraphTable.c +++ b/src/backend/rewrite/rewriteGraphTable.c @@ -101,6 +101,8 @@ static Query *generate_union_from_pathqueries(List **pathqueries); static List *get_path_elements_for_path_factor(Oid propgraphid, struct path_factor *pf); static bool is_property_associated_with_label(Oid labeloid, Oid propoid); static Node *get_element_property_expr(Oid elemoid, Oid propoid, int rtindex); +static bool graph_path_prefix_is_feasible_with_new_element(List *graph_path, struct path_element *new_pe); +static bool graph_path_edge_is_feasible(List *graph_path, struct path_element *edge_pe); /* * Convert GRAPH_TABLE clause into a subquery using relational @@ -367,9 +369,25 @@ generate_queries_for_path_pattern_recurse(RangeTblEntry *rte, List *pathqueries, foreach_ptr(struct path_element, pe, path_elems) { - /* Update current path being built with current element. */ + CHECK_FOR_INTERRUPTS(); + + /* + * Add the next selected element to the current path before checking + * feasibility, since the pruning logic inspects the resulting prefix + * using path-factor positions inside graph_path. + */ cur_path = lappend(cur_path, pe); + /* + * If the currently selected prefix already makes any edge unable to + * connect the adjacent selected vertices, abandon it right away. + */ + if (!graph_path_prefix_is_feasible_with_new_element(cur_path, pe)) + { + cur_path = list_delete_last(cur_path); + continue; + } + /* * If this is the last element in the path, generate query for the * completed path. Else recurse processing the next element. @@ -394,6 +412,77 @@ generate_queries_for_path_pattern_recurse(RangeTblEntry *rte, List *pathqueries, return pathqueries; } +/* + * Check whether appending the newest selected element can still lead to a + * valid graph path. + * + * Since the older prefix was already known to be feasible, the newly appended + * element can invalidate only the edge constraints it participates in. + */ +static bool +graph_path_prefix_is_feasible_with_new_element(List *graph_path, struct path_element *new_pe) +{ + struct path_factor *pf = new_pe->path_factor; + struct path_element *prev_pe; + + if (IS_EDGE_PATTERN(pf->kind)) + return graph_path_edge_is_feasible(graph_path, new_pe); + + Assert(pf->kind == VERTEX_PATTERN); + Assert(list_length(graph_path) > 0); + + if (list_length(graph_path) == 1) + return true; + + /* + * Repeated vertex variables are merged into one path factor before the + * DFS begins, so appending a vertex extends only the immediately + * preceding edge in the prefix. Any later edge referencing the same + * factor will be checked when that edge itself is appended. + */ + prev_pe = list_nth(graph_path, list_length(graph_path) - 2); + + if (!IS_EDGE_PATTERN(prev_pe->path_factor->kind)) + return true; + + return graph_path_edge_is_feasible(graph_path, prev_pe); +} + +/* + * Check whether the selected endpoints of an edge in the current path prefix + * still allow at least one valid direction for that edge. + */ +static bool +graph_path_edge_is_feasible(List *graph_path, struct path_element *edge_pe) +{ + struct path_factor *pf = edge_pe->path_factor; + int prefix_len = list_length(graph_path); + bool normal_ok = true; + bool reverse_ok = (pf->kind == EDGE_PATTERN_ANY); + + Assert(IS_EDGE_PATTERN(pf->kind)); + + if (pf->src_pf->factorpos < prefix_len) + { + struct path_element *src_pe; + + src_pe = list_nth(graph_path, pf->src_pf->factorpos); + normal_ok = normal_ok && src_pe->elemoid == edge_pe->srcvertexid; + reverse_ok = reverse_ok && src_pe->elemoid == edge_pe->destvertexid; + } + + if (pf->dest_pf->factorpos < prefix_len) + { + struct path_element *dest_pe; + + dest_pe = list_nth(graph_path, pf->dest_pf->factorpos); + normal_ok = normal_ok && dest_pe->elemoid == edge_pe->destvertexid; + reverse_ok = reverse_ok && dest_pe->elemoid == edge_pe->srcvertexid; + } + + return normal_ok || reverse_ok; +} + /* * Construct a query representing given graph path. * -- 2.41.0