You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@age.apache.org by de...@apache.org on 2021/10/22 23:14:13 UTC

[incubator-age] branch master updated: Add openCypher nodes() function AGE2-421

This is an automated email from the ASF dual-hosted git repository.

dehowef 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 bf35db2  Add openCypher nodes() function AGE2-421
bf35db2 is described below

commit bf35db2982b0e2f71925129513efa822c7b995cb
Author: Dehowe Feng <de...@gmail.com>
AuthorDate: Tue Oct 12 13:32:22 2021 -0700

    Add openCypher nodes() function AGE2-421
    
    Add the openCypher nodes() function.
    
    Added regression tests.
    
    Reorganized the keys implementation to be where the
    rest of the list functions were.
---
 age--0.6.0.sql                 | 26 ++++++++++-------
 regress/expected/expr.out      | 65 +++++++++++++++++++++++++++++++++---------
 regress/sql/expr.sql           | 16 +++++++++--
 src/backend/utils/adt/agtype.c | 61 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 141 insertions(+), 27 deletions(-)

diff --git a/age--0.6.0.sql b/age--0.6.0.sql
index ea6e595..8fd0d91 100644
--- a/age--0.6.0.sql
+++ b/age--0.6.0.sql
@@ -3333,17 +3333,6 @@ PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
 --
--- List functions -- return a list based on input
---
-
-CREATE FUNCTION ag_catalog.age_keys(agtype)
-RETURNS agtype
-LANGUAGE c
-STABLE
-PARALLEL SAFE
-AS 'MODULE_PATHNAME';
-
---
 -- Trig functions - radian input
 --
 CREATE FUNCTION ag_catalog.age_sin(variadic "any")
@@ -3748,6 +3737,13 @@ STRICT
 AS 'MODULE_PATHNAME';
 
 -- list functions
+CREATE FUNCTION ag_catalog.age_keys(agtype)
+RETURNS agtype
+LANGUAGE c
+STABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
 CREATE FUNCTION ag_catalog.age_labels(agtype)
 RETURNS agtype
 LANGUAGE c
@@ -3756,6 +3752,14 @@ RETURNS NULL ON NULL INPUT
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
+CREATE FUNCTION ag_catalog.age_nodes(agtype)
+RETURNS agtype
+LANGUAGE c
+STABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
 CREATE FUNCTION ag_catalog.age_relationships(agtype)
 RETURNS agtype
 LANGUAGE c
diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index c9e2155..33a72aa 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -5463,19 +5463,56 @@ NOTICE:  graph "list" has been created
  
 (1 row)
 
--- relationships()
-SELECT * from cypher('list', $$CREATE p=()-[:knows]->() RETURN p$$) as (path agtype);
-                                                                                                                               path                                                                                                                                
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- [{"id": 281474976710657, "label": "", "properties": {}}::vertex, {"id": 844424930131969, "label": "knows", "end_id": 281474976710658, "start_id": 281474976710657, "properties": {}}::edge, {"id": 281474976710658, "label": "", "properties": {}}::vertex]::path
+SELECT * from cypher('list', $$CREATE p=({name:"rick"})-[:knows]->({name:"morty"}) RETURN p$$) as (path agtype);
+                                                                                                                                              path                                                                                                                                              
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710657, "label": "", "properties": {"name": "rick"}}::vertex, {"id": 844424930131969, "label": "knows", "end_id": 281474976710658, "start_id": 281474976710657, "properties": {}}::edge, {"id": 281474976710658, "label": "", "properties": {"name": "morty"}}::vertex]::path
+(1 row)
+
+SELECT * from cypher('list', $$CREATE p=({name:'rachael'})-[:knows]->({name:'monica'})-[:knows]->({name:'phoebe'}) RETURN p$$) as (path agtype);
+                                                                                                                                                                                                                                                     path                                                                                                                                                                                                                                                      
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710659, "label": "", "properties": {"name": "rachael"}}::vertex, {"id": 844424930131971, "label": "knows", "end_id": 281474976710660, "start_id": 281474976710659, "properties": {}}::edge, {"id": 281474976710660, "label": "", "properties": {"name": "monica"}}::vertex, {"id": 844424930131970, "label": "knows", "end_id": 281474976710661, "start_id": 281474976710660, "properties": {}}::edge, {"id": 281474976710661, "label": "", "properties": {"name": "phoebe"}}::vertex]::path
+(1 row)
+
+-- nodes()
+SELECT * from cypher('list', $$MATCH p=()-[]->() RETURN nodes(p)$$) as (nodes agtype);
+                                                                               nodes                                                                               
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710657, "label": "", "properties": {"name": "rick"}}::vertex, {"id": 281474976710658, "label": "", "properties": {"name": "morty"}}::vertex]
+ [{"id": 281474976710659, "label": "", "properties": {"name": "rachael"}}::vertex, {"id": 281474976710660, "label": "", "properties": {"name": "monica"}}::vertex]
+ [{"id": 281474976710660, "label": "", "properties": {"name": "monica"}}::vertex, {"id": 281474976710661, "label": "", "properties": {"name": "phoebe"}}::vertex]
+(3 rows)
+
+SELECT * from cypher('list', $$MATCH p=()-[]->()-[]->() RETURN nodes(p)$$) as (nodes agtype);
+                                                                                                                       nodes                                                                                                                       
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710659, "label": "", "properties": {"name": "rachael"}}::vertex, {"id": 281474976710660, "label": "", "properties": {"name": "monica"}}::vertex, {"id": 281474976710661, "label": "", "properties": {"name": "phoebe"}}::vertex]
 (1 row)
 
-SELECT * from cypher('list', $$CREATE p=()-[:knows]->()-[:knows]->() RETURN p$$) as (path agtype);
-                                                                                                                                                                                                                             path                                                                                                                                                                                                                             
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- [{"id": 281474976710659, "label": "", "properties": {}}::vertex, {"id": 844424930131971, "label": "knows", "end_id": 281474976710660, "start_id": 281474976710659, "properties": {}}::edge, {"id": 281474976710660, "label": "", "properties": {}}::vertex, {"id": 844424930131970, "label": "knows", "end_id": 281474976710661, "start_id": 281474976710660, "properties": {}}::edge, {"id": 281474976710661, "label": "", "properties": {}}::vertex]::path
+-- should return nothing
+SELECT * from cypher('list', $$MATCH p=()-[]->()-[]->()-[]->() RETURN nodes(p)$$) as (nodes agtype);
+ nodes 
+-------
+(0 rows)
+
+-- should return SQL NULL
+SELECT * from cypher('list', $$RETURN nodes(NULL)$$) as (nodes agtype);
+ nodes 
+-------
+ 
 (1 row)
 
+-- should return an error
+SELECT * from cypher('list', $$MATCH (u) RETURN nodes([1,2,3])$$) as (nodes agtype);
+ERROR:  nodes() argument must resolve to a scalar value
+SELECT * from cypher('list', $$MATCH (u) RETURN nodes("string")$$) as (nodes agtype);
+ERROR:  nodes() argument must be a path
+SELECT * from cypher('list', $$MATCH (u) RETURN nodes(u)$$) as (nodes agtype);
+ERROR:  nodes() argument must be a path
+SELECT * from cypher('list', $$MATCH (u)-[]->() RETURN nodes(u)$$) as (nodes agtype);
+ERROR:  nodes() argument must be a path
+-- relationships()
 SELECT * from cypher('list', $$MATCH p=()-[]->() RETURN relationships(p)$$) as (relationships agtype);
                                                         relationships                                                        
 -----------------------------------------------------------------------------------------------------------------------------
@@ -5615,11 +5652,11 @@ SELECT * from cypher('list', $$CREATE (u:Cars {name: "MR2"}) RETURN u$$) as (Ver
 SELECT * from cypher('list', $$MATCH (u) RETURN labels(u), u$$) as (Labels agtype, Vertices agtype);
    labels   |                                       vertices                                       
 ------------+--------------------------------------------------------------------------------------
- [""]       | {"id": 281474976710657, "label": "", "properties": {}}::vertex
- [""]       | {"id": 281474976710658, "label": "", "properties": {}}::vertex
- [""]       | {"id": 281474976710659, "label": "", "properties": {}}::vertex
- [""]       | {"id": 281474976710660, "label": "", "properties": {}}::vertex
- [""]       | {"id": 281474976710661, "label": "", "properties": {}}::vertex
+ [""]       | {"id": 281474976710657, "label": "", "properties": {"name": "rick"}}::vertex
+ [""]       | {"id": 281474976710658, "label": "", "properties": {"name": "morty"}}::vertex
+ [""]       | {"id": 281474976710659, "label": "", "properties": {"name": "rachael"}}::vertex
+ [""]       | {"id": 281474976710660, "label": "", "properties": {"name": "monica"}}::vertex
+ [""]       | {"id": 281474976710661, "label": "", "properties": {"name": "phoebe"}}::vertex
  ["People"] | {"id": 1125899906842625, "label": "People", "properties": {"name": "John"}}::vertex
  ["People"] | {"id": 1125899906842626, "label": "People", "properties": {"name": "Larry"}}::vertex
  ["Cars"]   | {"id": 1407374883553281, "label": "Cars", "properties": {"name": "G35"}}::vertex
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index 509e661..f26ba14 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -2235,9 +2235,21 @@ SELECT * from cypher('keys', $$RETURN keys("string")$$) as (keys agtype);
 SELECT * from cypher('keys', $$MATCH u=()-[]-() RETURN keys(u)$$) as (keys agtype);
 
 SELECT create_graph('list');
+SELECT * from cypher('list', $$CREATE p=({name:"rick"})-[:knows]->({name:"morty"}) RETURN p$$) as (path agtype);
+SELECT * from cypher('list', $$CREATE p=({name:'rachael'})-[:knows]->({name:'monica'})-[:knows]->({name:'phoebe'}) RETURN p$$) as (path agtype);
+-- nodes()
+SELECT * from cypher('list', $$MATCH p=()-[]->() RETURN nodes(p)$$) as (nodes agtype);
+SELECT * from cypher('list', $$MATCH p=()-[]->()-[]->() RETURN nodes(p)$$) as (nodes agtype);
+-- should return nothing
+SELECT * from cypher('list', $$MATCH p=()-[]->()-[]->()-[]->() RETURN nodes(p)$$) as (nodes agtype);
+-- should return SQL NULL
+SELECT * from cypher('list', $$RETURN nodes(NULL)$$) as (nodes agtype);
+-- should return an error
+SELECT * from cypher('list', $$MATCH (u) RETURN nodes([1,2,3])$$) as (nodes agtype);
+SELECT * from cypher('list', $$MATCH (u) RETURN nodes("string")$$) as (nodes agtype);
+SELECT * from cypher('list', $$MATCH (u) RETURN nodes(u)$$) as (nodes agtype);
+SELECT * from cypher('list', $$MATCH (u)-[]->() RETURN nodes(u)$$) as (nodes agtype);
 -- relationships()
-SELECT * from cypher('list', $$CREATE p=()-[:knows]->() RETURN p$$) as (path agtype);
-SELECT * from cypher('list', $$CREATE p=()-[:knows]->()-[:knows]->() RETURN p$$) as (path agtype);
 SELECT * from cypher('list', $$MATCH p=()-[]->() RETURN relationships(p)$$) as (relationships agtype);
 SELECT * from cypher('list', $$MATCH p=()-[]->()-[]->() RETURN relationships(p)$$) as (relationships agtype);
 -- should return nothing
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index b72cb53..329cd34 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -8421,6 +8421,67 @@ Datum age_keys(PG_FUNCTION_ARGS)
     PG_RETURN_POINTER(agtype_value_to_agtype(agtv_result));
 }
 
+PG_FUNCTION_INFO_V1(age_nodes);
+/*
+ * Execution function to implement openCypher nodes() function
+ */
+Datum age_nodes(PG_FUNCTION_ARGS)
+{
+    agtype *agt_arg = NULL;
+    agtype_value *agtv_path = NULL;
+    agtype_in_state agis_result;
+    int i = 0;
+
+    /* check for null */
+    if (PG_ARGISNULL(0))
+    {
+        PG_RETURN_NULL();
+    }
+
+    agt_arg = AG_GET_ARG_AGTYPE_P(0);
+    /* check for a scalar object */
+    if (!AGT_ROOT_IS_SCALAR(agt_arg))
+    {
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("nodes() argument must resolve to a scalar value")));
+    }
+
+    /* get the potential path out of the array */
+    agtv_path = get_ith_agtype_value_from_container(&agt_arg->root, 0);
+
+    /* is it an agtype null? */
+    if (agtv_path->type == AGTV_NULL)
+    {
+            PG_RETURN_NULL();
+    }
+
+    /* verify that it is an agtype path */
+    if (agtv_path->type != AGTV_PATH)
+        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                        errmsg("nodes() argument must be a path")));
+
+    /* clear the result structure */
+    MemSet(&agis_result, 0, sizeof(agtype_in_state));
+
+    /* push the beginning of the array */
+    agis_result.res = push_agtype_value(&agis_result.parse_state,
+                                        WAGT_BEGIN_ARRAY, NULL);
+    /* push in each vertex (every other entry) from the path */
+    for (i = 0; i < agtv_path->val.array.num_elems; i += 2)
+    {
+        agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM,
+                                            &agtv_path->val.array.elems[i]);
+    }
+
+    /* push the end of the array */
+    agis_result.res = push_agtype_value(&agis_result.parse_state,
+                                        WAGT_END_ARRAY, NULL);
+
+    /* convert the agtype_value to a datum to return to the caller */
+    PG_RETURN_POINTER(agtype_value_to_agtype(agis_result.res));
+}
+
 PG_FUNCTION_INFO_V1(age_labels);
 /*
  * Execution function to implement openCypher labels() function