Skip to content

Commit

Permalink
Allow a path of one vertex
Browse files Browse the repository at this point in the history
Add in the logic to allow a path of one vertex. This will bring AGE
paths in line with others who use the openCypher specification,
namely, Neo4j.

Adjust regression tests.
  • Loading branch information
jrgemignani committed Jan 27, 2022
1 parent 20ef382 commit 9fdfe40
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 31 deletions.
6 changes: 2 additions & 4 deletions regress/expected/agtype.out
Original file line number Diff line number Diff line change
Expand Up @@ -2378,8 +2378,7 @@ SELECT _agtype_build_path(
_agtype_build_edge('1'::graphid, '2'::graphid, '3'::graphid,
$$label$$, agtype_build_map('id', 2))
);
ERROR: paths consist of alternating vertices and edges
HINT: paths require at least 2 vertices and 1 edge
ERROR: a path is of the form: [vertex, (edge, vertex)*i] where i >= 0
SELECT _agtype_build_path(
_agtype_build_vertex('2'::graphid, $$label_name$$, agtype_build_map()),
_agtype_build_edge('1'::graphid, '2'::graphid, '3'::graphid,
Expand All @@ -2388,8 +2387,7 @@ SELECT _agtype_build_path(
_agtype_build_edge('1'::graphid, '4'::graphid, '5'::graphid,
$$label$$, agtype_build_map('id', 2))
);
ERROR: paths consist of alternating vertices and edges
HINT: paths require an odd number of elements
ERROR: a path is of the form: [vertex, (edge, vertex)*i] where i >= 0
SELECT _agtype_build_path(
_agtype_build_vertex('2'::graphid, $$label_name$$, agtype_build_map()),
_agtype_build_edge('1'::graphid, '2'::graphid, '3'::graphid,
Expand Down
11 changes: 6 additions & 5 deletions regress/expected/cypher_create.out
Original file line number Diff line number Diff line change
Expand Up @@ -518,15 +518,16 @@ $$) as (a agtype);
ERROR: variable b already exists
LINE 1: SELECT * FROM cypher('cypher_create', $$
^
-- Not a valid path
-- A valid single vertex path
SELECT * FROM cypher('cypher_create', $$
CREATE p=(a)
RETURN p
$$) as (a agtype);
ERROR: paths consist of alternating vertices and edges.
LINE 2: CREATE p=(a)
^
HINT: paths require at least 2 vertices and 1 edge
a
------------------------------------------------------------------------
[{"id": 281474976710677, "label": "", "properties": {}}::vertex]::path
(1 row)

--CREATE with joins
SELECT *
FROM cypher('cypher_create', $$
Expand Down
24 changes: 19 additions & 5 deletions regress/expected/cypher_match.out
Original file line number Diff line number Diff line change
Expand Up @@ -528,15 +528,29 @@ ERROR: label vmissing does not exists
LINE 1: SELECT * FROM cypher('cypher_match', $$MATCH (n:vmissing)-[]...
^
--
--Errors
-- Path of one vertex. This should select 14
--
SELECT * FROM cypher('cypher_match', $$
MATCH p=() RETURN p
$$) AS (p agtype);
ERROR: paths consist of alternating vertices and edges.
LINE 2: MATCH p=() RETURN p
^
HINT: paths require at least 2 vertices and 1 edge
p
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
[{"id": 281474976710657, "label": "", "properties": {"int_key": 1, "map_key": {"key": "value"}, "list_key": [1, 2, 3], "float_key": 3.14, "string_key": "test"}}::vertex]::path
[{"id": 281474976710658, "label": "", "properties": {"lst": [1, null, 3.14, "string", {"key": "value"}, []]}}::vertex]::path
[{"id": 844424930131969, "label": "v", "properties": {}}::vertex]::path
[{"id": 844424930131970, "label": "v", "properties": {"i": 0}}::vertex]::path
[{"id": 844424930131971, "label": "v", "properties": {"i": 1}}::vertex]::path
[{"id": 1125899906842625, "label": "v1", "properties": {"id": "initial"}}::vertex]::path
[{"id": 1125899906842626, "label": "v1", "properties": {"id": "middle"}}::vertex]::path
[{"id": 1125899906842627, "label": "v1", "properties": {"id": "end"}}::vertex]::path
[{"id": 1688849860263937, "label": "v2", "properties": {"id": "initial"}}::vertex]::path
[{"id": 1688849860263938, "label": "v2", "properties": {"id": "middle"}}::vertex]::path
[{"id": 1688849860263939, "label": "v2", "properties": {"id": "end"}}::vertex]::path
[{"id": 2251799813685249, "label": "v3", "properties": {"id": "initial"}}::vertex]::path
[{"id": 2251799813685250, "label": "v3", "properties": {"id": "middle"}}::vertex]::path
[{"id": 2251799813685251, "label": "v3", "properties": {"id": "end"}}::vertex]::path
(14 rows)

--
-- MATCH with WHERE EXISTS(pattern)
--
Expand Down
2 changes: 1 addition & 1 deletion regress/sql/cypher_create.sql
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ SELECT * FROM cypher('cypher_create', $$
CREATE (a)-[b:e_var]->()
$$) as (a agtype);

-- Not a valid path
-- A valid single vertex path
SELECT * FROM cypher('cypher_create', $$
CREATE p=(a)
RETURN p
Expand Down
2 changes: 1 addition & 1 deletion regress/sql/cypher_match.sql
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ SELECT * FROM cypher('cypher_match', $$MATCH (n:e1)-[]-() RETURN n$$) AS (n agty
SELECT * FROM cypher('cypher_match', $$MATCH (n:vmissing)-[]-() RETURN n$$) AS (n agtype);

--
--Errors
-- Path of one vertex. This should select 14
--
SELECT * FROM cypher('cypher_match', $$
MATCH p=() RETURN p
Expand Down
14 changes: 6 additions & 8 deletions src/backend/parser/cypher_clause.c
Original file line number Diff line number Diff line change
Expand Up @@ -3613,13 +3613,12 @@ transform_match_create_path_variable(cypher_parsestate *cpstate,
List *entity_exprs = NIL;
ListCell *lc;

if (list_length(entities) < 3)
if (list_length(entities) < 1)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("paths consist of alternating vertices and edges."),
parser_errposition(pstate, path->location),
errhint("paths require at least 2 vertices and 1 edge")));
errmsg("paths require at least 1 vertex"),
parser_errposition(pstate, path->location)));
}

// extract the expr for each entity
Expand Down Expand Up @@ -4267,13 +4266,12 @@ transform_cypher_create_path(cypher_parsestate *cpstate, List **target_list,
{
TargetEntry *te;

if (list_length(transformed_path) < 3)
if (list_length(transformed_path) < 1)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("paths consist of alternating vertices and edges."),
parser_errposition(pstate, path->location),
errhint("paths require at least 2 vertices and 1 edge")));
errmsg("paths require at least 1 vertex"),
parser_errposition(pstate, path->location)));
}

te = placeholder_target_entry(cpstate, path->var_name);
Expand Down
22 changes: 15 additions & 7 deletions src/backend/utils/adt/agtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -1780,20 +1780,18 @@ Datum _agtype_build_path(PG_FUNCTION_ARGS)
/* build argument values to build the object */
nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);

if (nargs < 3)
if (nargs < 1)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("paths consist of alternating vertices and edges"),
errhint("paths require at least 2 vertices and 1 edge")));
errmsg("paths require at least 1 vertex")));
}

if (nargs % 2 == 0)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("paths consist of alternating vertices and edges"),
errhint("paths require an odd number of elements")));
errmsg("a path is of the form: [vertex, (edge, vertex)*i] where i >= 0")));
}

/*
Expand Down Expand Up @@ -1935,10 +1933,20 @@ Datum make_path(List *path)

result.res = push_agtype_value(&result.parse_state, WAGT_BEGIN_ARRAY, NULL);

if (list_length(path) < 3 || list_length(path) % 2 != 1)
if (list_length(path) < 1)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("paths require at least 1 vertex")));
}

if (list_length(path) % 2 != 1)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("path list is not a valid path")));
errmsg("a path is of the form: [vertex, (edge, vertex)*i] where i >= 0")));
}


foreach (lc, path)
{
Expand Down

0 comments on commit 9fdfe40

Please sign in to comment.