diff --git a/age--1.2.0.sql b/age--1.2.0.sql index e50f1a628..c8d7ecd0b 100644 --- a/age--1.2.0.sql +++ b/age--1.2.0.sql @@ -4162,10 +4162,10 @@ IMMUTABLE PARALLEL SAFE AS 'MODULE_PATHNAME'; -CREATE FUNCTION ag_catalog.age_unnest(agtype, block_types boolean = false) - RETURNS SETOF agtype - LANGUAGE c - IMMUTABLE +CREATE FUNCTION ag_catalog.age_unnest(agtype) +RETURNS SETOF agtype +LANGUAGE c +IMMUTABLE PARALLEL SAFE AS 'MODULE_PATHNAME'; diff --git a/regress/expected/cypher_unwind.out b/regress/expected/cypher_unwind.out index d0a71717a..3c454f100 100644 --- a/regress/expected/cypher_unwind.out +++ b/regress/expected/cypher_unwind.out @@ -25,25 +25,48 @@ NOTICE: graph "cypher_unwind" has been created (1 row) +-- Create nodes and relations SELECT * FROM cypher('cypher_unwind', $$ - UNWIND [1, 2, 3] AS i RETURN i + CREATE (n {name: 'node1', a: [1, 2, 3]}), + (m {name: 'node2', a: [4, 5, 6]}), + (o {name: 'node3', a: [7, 8, 9]}), + (n)-[:KNOWS]->(m), + (m)-[:KNOWS]->(o) $$) as (i agtype); i --- - 1 - 2 - 3 +(0 rows) + +SELECT * FROM cypher('cypher_unwind', $$ + MATCH (n) + RETURN n +$$) as (i agtype); + i +----------------------------------------------------------------------------------------------- + {"id": 281474976710657, "label": "", "properties": {"a": [1, 2, 3], "name": "node1"}}::vertex + {"id": 281474976710658, "label": "", "properties": {"a": [4, 5, 6], "name": "node2"}}::vertex + {"id": 281474976710659, "label": "", "properties": {"a": [7, 8, 9], "name": "node3"}}::vertex (3 rows) +-- +-- Test UNWIND clause +-- SELECT * FROM cypher('cypher_unwind', $$ - CREATE ({a: [1, 2, 3]}), ({a: [4, 5, 6]}) + UNWIND [1, 2, 3] AS i + RETURN i $$) as (i agtype); i --- -(0 rows) + 1 + 2 + 3 +(3 rows) SELECT * FROM cypher('cypher_unwind', $$ - MATCH (n) WITH n.a AS a UNWIND a AS i RETURN * + MATCH (n) + WITH n.a AS a + UNWIND a AS i + RETURN * $$) as (i agtype, j agtype); i | j -----------+--- @@ -53,7 +76,10 @@ $$) as (i agtype, j agtype); [4, 5, 6] | 4 [4, 5, 6] | 5 [4, 5, 6] | 6 -(6 rows) + [7, 8, 9] | 7 + [7, 8, 9] | 8 + [7, 8, 9] | 9 +(9 rows) SELECT * FROM cypher('cypher_unwind', $$ WITH [[1, 2], [3, 4], 5] AS nested @@ -70,17 +96,118 @@ $$) as (i agtype); 5 (5 rows) +-- UNWIND vertices +SELECT * FROM cypher('cypher_unwind', $$ + MATCH p=(n)-[:KNOWS]->(m) + UNWIND nodes(p) as node + RETURN node +$$) as (i agtype); + i +----------------------------------------------------------------------------------------------- + {"id": 281474976710657, "label": "", "properties": {"a": [1, 2, 3], "name": "node1"}}::vertex + {"id": 281474976710658, "label": "", "properties": {"a": [4, 5, 6], "name": "node2"}}::vertex + {"id": 281474976710658, "label": "", "properties": {"a": [4, 5, 6], "name": "node2"}}::vertex + {"id": 281474976710659, "label": "", "properties": {"a": [7, 8, 9], "name": "node3"}}::vertex +(4 rows) + SELECT * FROM cypher('cypher_unwind', $$ - WITH [{id: 0, label:'', properties:{}}::vertex, {id: 1, label:'', properties:{}}::vertex] as n - UNWIND n as a - SET a.i = 1 - RETURN a + MATCH p=(n)-[:KNOWS]->(m) + UNWIND nodes(p) as node + RETURN node.name $$) as (i agtype); -ERROR: UNWIND clause does not support agtype vertex + i +--------- + "node1" + "node2" + "node2" + "node3" +(4 rows) + +-- UNWIND edges +SELECT * FROM cypher('cypher_unwind', $$ + MATCH p=(n)-[:KNOWS]->(m) + UNWIND relationships(p) as relation + RETURN relation +$$) as (i agtype); + i +--------------------------------------------------------------------------------------------------------------------------- + {"id": 844424930131969, "label": "KNOWS", "end_id": 281474976710658, "start_id": 281474976710657, "properties": {}}::edge + {"id": 844424930131970, "label": "KNOWS", "end_id": 281474976710659, "start_id": 281474976710658, "properties": {}}::edge +(2 rows) + +SELECT * FROM cypher('cypher_unwind', $$ + MATCH p=(n)-[:KNOWS]->(m) + UNWIND relationships(p) as relation + RETURN type(relation) +$$) as (i agtype); + i +--------- + "KNOWS" + "KNOWS" +(2 rows) + +-- UNWIND paths (vle) +SELECT * FROM cypher('cypher_unwind', $$ + MATCH p=({name:'node1'})-[e:KNOWS*]->({name:'node3'}) + UNWIND [p] as path + RETURN path +$$) as (i agtype); + i +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 281474976710657, "label": "_ag_label_vertex", "properties": {"a": [1, 2, 3], "name": "node1"}}::vertex, {"id": 844424930131969, "label": "KNOWS", "end_id": 281474976710658, "start_id": 281474976710657, "properties": {}}::edge, {"id": 281474976710658, "label": "_ag_label_vertex", "properties": {"a": [4, 5, 6], "name": "node2"}}::vertex, {"id": 844424930131970, "label": "KNOWS", "end_id": 281474976710659, "start_id": 281474976710658, "properties": {}}::edge, {"id": 281474976710659, "label": "_ag_label_vertex", "properties": {"a": [7, 8, 9], "name": "node3"}}::vertex]::path +(1 row) + +SELECT * FROM cypher('cypher_unwind', $$ + MATCH p=({name:'node1'})-[e:KNOWS*]->({name:'node3'}) + UNWIND [p] as path + RETURN relationships(path) +$$) as (i agtype); + i +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 844424930131969, "label": "KNOWS", "end_id": 281474976710658, "start_id": 281474976710657, "properties": {}}::edge, {"id": 844424930131970, "label": "KNOWS", "end_id": 281474976710659, "start_id": 281474976710658, "properties": {}}::edge] +(1 row) + +SELECT * FROM cypher('cypher_unwind', $$ + MATCH p=({name:'node1'})-[e:KNOWS*]->({name:'node3'}) + UNWIND [p] as path + UNWIND relationships(path) as edge + RETURN edge +$$) as (i agtype); + i +--------------------------------------------------------------------------------------------------------------------------- + {"id": 844424930131969, "label": "KNOWS", "end_id": 281474976710658, "start_id": 281474976710657, "properties": {}}::edge + {"id": 844424930131970, "label": "KNOWS", "end_id": 281474976710659, "start_id": 281474976710658, "properties": {}}::edge +(2 rows) + +-- Unwind with SET clause +SELECT * FROM cypher('cypher_unwind', $$ + MATCH p=(n)-[:KNOWS]->(m) + UNWIND nodes(p) as node + SET node.type = 'vertex' +$$) as (i agtype); + i +--- +(0 rows) + +SELECT * FROM cypher('cypher_unwind', $$ + MATCH (n) + RETURN n +$$) as (i agtype); + i +----------------------------------------------------------------------------------------------------------------- + {"id": 281474976710657, "label": "", "properties": {"a": [1, 2, 3], "name": "node1", "type": "vertex"}}::vertex + {"id": 281474976710658, "label": "", "properties": {"a": [4, 5, 6], "name": "node2", "type": "vertex"}}::vertex + {"id": 281474976710659, "label": "", "properties": {"a": [7, 8, 9], "name": "node3", "type": "vertex"}}::vertex +(3 rows) + +-- +-- Clean up +-- SELECT drop_graph('cypher_unwind', true); -NOTICE: drop cascades to 2 other objects +NOTICE: drop cascades to 3 other objects DETAIL: drop cascades to table cypher_unwind._ag_label_vertex drop cascades to table cypher_unwind._ag_label_edge +drop cascades to table cypher_unwind."KNOWS" NOTICE: graph "cypher_unwind" has been dropped drop_graph ------------ diff --git a/regress/sql/cypher_unwind.sql b/regress/sql/cypher_unwind.sql index d1f2d81fb..8e8c74bc9 100644 --- a/regress/sql/cypher_unwind.sql +++ b/regress/sql/cypher_unwind.sql @@ -22,16 +22,35 @@ SET search_path TO ag_catalog; SELECT create_graph('cypher_unwind'); +-- Create nodes and relations + SELECT * FROM cypher('cypher_unwind', $$ - UNWIND [1, 2, 3] AS i RETURN i + CREATE (n {name: 'node1', a: [1, 2, 3]}), + (m {name: 'node2', a: [4, 5, 6]}), + (o {name: 'node3', a: [7, 8, 9]}), + (n)-[:KNOWS]->(m), + (m)-[:KNOWS]->(o) $$) as (i agtype); SELECT * FROM cypher('cypher_unwind', $$ - CREATE ({a: [1, 2, 3]}), ({a: [4, 5, 6]}) + MATCH (n) + RETURN n +$$) as (i agtype); + +-- +-- Test UNWIND clause +-- + +SELECT * FROM cypher('cypher_unwind', $$ + UNWIND [1, 2, 3] AS i + RETURN i $$) as (i agtype); SELECT * FROM cypher('cypher_unwind', $$ - MATCH (n) WITH n.a AS a UNWIND a AS i RETURN * + MATCH (n) + WITH n.a AS a + UNWIND a AS i + RETURN * $$) as (i agtype, j agtype); SELECT * FROM cypher('cypher_unwind', $$ @@ -41,11 +60,70 @@ SELECT * FROM cypher('cypher_unwind', $$ RETURN y $$) as (i agtype); +-- UNWIND vertices + +SELECT * FROM cypher('cypher_unwind', $$ + MATCH p=(n)-[:KNOWS]->(m) + UNWIND nodes(p) as node + RETURN node +$$) as (i agtype); + +SELECT * FROM cypher('cypher_unwind', $$ + MATCH p=(n)-[:KNOWS]->(m) + UNWIND nodes(p) as node + RETURN node.name +$$) as (i agtype); + +-- UNWIND edges + +SELECT * FROM cypher('cypher_unwind', $$ + MATCH p=(n)-[:KNOWS]->(m) + UNWIND relationships(p) as relation + RETURN relation +$$) as (i agtype); + +SELECT * FROM cypher('cypher_unwind', $$ + MATCH p=(n)-[:KNOWS]->(m) + UNWIND relationships(p) as relation + RETURN type(relation) +$$) as (i agtype); + +-- UNWIND paths (vle) + +SELECT * FROM cypher('cypher_unwind', $$ + MATCH p=({name:'node1'})-[e:KNOWS*]->({name:'node3'}) + UNWIND [p] as path + RETURN path +$$) as (i agtype); + +SELECT * FROM cypher('cypher_unwind', $$ + MATCH p=({name:'node1'})-[e:KNOWS*]->({name:'node3'}) + UNWIND [p] as path + RETURN relationships(path) +$$) as (i agtype); + SELECT * FROM cypher('cypher_unwind', $$ - WITH [{id: 0, label:'', properties:{}}::vertex, {id: 1, label:'', properties:{}}::vertex] as n - UNWIND n as a - SET a.i = 1 - RETURN a + MATCH p=({name:'node1'})-[e:KNOWS*]->({name:'node3'}) + UNWIND [p] as path + UNWIND relationships(path) as edge + RETURN edge $$) as (i agtype); +-- Unwind with SET clause + +SELECT * FROM cypher('cypher_unwind', $$ + MATCH p=(n)-[:KNOWS]->(m) + UNWIND nodes(p) as node + SET node.type = 'vertex' +$$) as (i agtype); + +SELECT * FROM cypher('cypher_unwind', $$ + MATCH (n) + RETURN n +$$) as (i agtype); + +-- +-- Clean up +-- + SELECT drop_graph('cypher_unwind', true); \ No newline at end of file diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c index e1e6c94be..cee75f764 100644 --- a/src/backend/parser/cypher_clause.c +++ b/src/backend/parser/cypher_clause.c @@ -1331,7 +1331,7 @@ static Query *transform_cypher_unwind(cypher_parsestate *cpstate, old_expr_kind = pstate->p_expr_kind; pstate->p_expr_kind = EXPR_KIND_SELECT_TARGET; funcexpr = ParseFuncOrColumn(pstate, unwind->funcname, - list_make2(expr, makeBoolConst(true, false)), + list_make1(expr), pstate->p_last_srf, unwind, false, target_syntax_loc); diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c index ed4bf4d47..cba3316b2 100644 --- a/src/backend/utils/adt/agtype.c +++ b/src/backend/utils/adt/agtype.c @@ -10031,14 +10031,11 @@ Datum age_range(PG_FUNCTION_ARGS) PG_FUNCTION_INFO_V1(age_unnest); /* * Function to convert the Array type of Agtype into each row. It is used for - * Cypher `UNWIND` clause, but considering the situation in which the user can - * directly use this function in vanilla PGSQL, put a second parameter related - * to this. + * Cypher `UNWIND` clause. */ Datum age_unnest(PG_FUNCTION_ARGS) { agtype *agtype_arg = AG_GET_ARG_AGTYPE_P(0); - bool block_types = PG_GETARG_BOOL(1); ReturnSetInfo *rsi; Tuplestorestate *tuple_store; TupleDesc tupdesc; @@ -10090,15 +10087,6 @@ Datum age_unnest(PG_FUNCTION_ARGS) bool nulls[1] = {false}; agtype *val = agtype_value_to_agtype(&v); - if (block_types && ( - v.type == AGTV_VERTEX || v.type == AGTV_EDGE || v.type == AGTV_PATH)) - { - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("UNWIND clause does not support agtype %s", - agtype_value_type_to_string(v.type)))); - } - /* use the tmp context so we can clean up after each tuple is done */ old_cxt = MemoryContextSwitchTo(tmp_cxt);