Add shortest_path / all_shortest_paths SRFs#2430
Conversation
0e591ad to
06ccd1e
Compare
06ccd1e to
739c352
Compare
Add two C set-returning functions that compute unweighted (hop-count)
shortest paths over the cached global graph adjacency via BFS, callable
both at the SQL top level and inside a cypher() RETURN:
- age_shortest_path(...) -> the single shortest path (0 or 1 rows)
- age_all_shortest_paths(...) -> every shortest path, one per row
The signature follows the natural Cypher argument order
(graph, start, end, edge_types, direction, min_hops, max_hops), registered
in sql/agtype_typecast.sql (install) and age--1.7.0--y.y.y.sql (upgrade).
Unimplemented parameters fail loudly: multiple relationship types and a
non-zero min_hops raise ERRCODE_FEATURE_NOT_SUPPORTED. A single edge type
(string or one-element array) is honored, and a NULL endpoint yields no
rows per Cypher null semantics (wrong-typed endpoints / NULL graph still
error).
To call the SRFs inside a cypher() RETURN, transform_cypher_return now sets
query->hasTargetSRFs (it was the only results-producing clause that didn't,
so the planner never added a ProjectSet node), and transform_FuncCall
auto-prepends the graph name for snake_case shortest_path /
all_shortest_paths. camelCase names are reserved for the future native
grammar.
Robustness:
- BFS guards against non-existent endpoints (returns 0 rows instead of
crashing) and honors CHECK_FOR_INTERRUPTS.
- An unknown edge label now matches no edges instead of silently
traversing all of them (get_label_relation returns InvalidOid).
Adds the age_shortest_path regression test (directed/undirected, label
filtering, parallel edges, self-loops, max_hops, the not-supported stubs,
NULL and non-existent endpoint/graph guards).
38/38 installcheck pass.
Co-authored-by: Copilot <copilot@github.com>
modified: Makefile
modified: age--1.7.0--y.y.y.sql
modified: sql/agtype_typecast.sql
modified: src/backend/parser/cypher_clause.c
modified: src/backend/parser/cypher_expr.c
modified: src/backend/utils/adt/age_vle.c
new file: regress/expected/age_shortest_path.out
new file: regress/sql/age_shortest_path.sql
739c352 to
15ce971
Compare
|
Reviewed the full diff — traced the BFS/enumeration against the regression cases and confirmed the supporting helpers. This is solid, well-tested work; I'd merge it after a couple of minor cleanups. (I read the code carefully but did not rebuild locally — the 37/37 installcheck result is the author's.) Strengths
Suggestions (non-blocking)
Nice work — the crash guard and the test matrix in particular are well done. |
gregfelice
left a comment
There was a problem hiding this comment.
Approving. Source-level review posted above; the implementation is correct (BFS shortest-depth, all-paths predecessor DAG, the non-existent-endpoint crash guard) and well-tested.
Verified the cassert PG18 gate locally (CI-mirror: REL_18_STABLE --enable-cassert -O0, -Werror clean build, EXTRA_TESTS="pgvector fuzzystrmatch pg_trgm"): All 42 tests passed, including the new age_shortest_path test, no regression.diffs.
The four notes in my review (error-message prefix on the all-paths function, unbounded result count for age_all_shortest_paths, scratch-memory lifetime, and trimming the stale parts of the description) are all non-blocking — fine as follow-ups.
Add two C set-returning functions that compute unweighted (hop-count) shortest paths over the cached global graph adjacency via BFS, callable both at the SQL top level and inside a cypher() RETURN:
The signature follows the natural Cypher argument order (graph, start, end, edge_types, direction, min_hops, max_hops), registered in sql/agtype_typecast.sql (install) and age--1.7.0--y.y.y.sql (upgrade). Unimplemented parameters fail loudly: multiple relationship types and a non-zero min_hops raise ERRCODE_FEATURE_NOT_SUPPORTED. A single edge type (string or one-element array) is honored, and a NULL endpoint yields no rows per Cypher null semantics (wrong-typed endpoints / NULL graph still error).
To call the SRFs inside a cypher() RETURN, transform_cypher_return now sets query->hasTargetSRFs (it was the only results-producing clause that didn't, so the planner never added a ProjectSet node), and transform_FuncCall auto-prepends the graph name for snake_case shortest_path / all_shortest_paths. camelCase names are reserved for the future native grammar.
Robustness:
Adds the age_shortest_path regression test (directed/undirected, label filtering, parallel edges, self-loops, max_hops, the not-supported stubs, NULL and non-existent endpoint/graph guards). 37/37 installcheck pass.
Co-authored-by: Claude noreply@anthropic.com
modified: Makefile
modified: age--1.7.0--y.y.y.sql
new file: regress/expected/age_shortest_path.out
new file: regress/sql/age_shortest_path.sql
modified: sql/agtype_typecast.sql
modified: src/backend/parser/cypher_clause.c
modified: src/backend/parser/cypher_expr.c
modified: src/backend/utils/adt/age_global_graph.c
modified: src/backend/utils/adt/age_vle.c