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

[incubator-age] branch master updated: Add openCypher labels() function AGE2-420

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

jgemignani 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 385245d  Add openCypher labels() function AGE2-420
385245d is described below

commit 385245d98db4bc4e6d0416e393231ae338bb9897
Author: John Gemignani <jr...@gmail.com>
AuthorDate: Wed Oct 6 17:53:09 2021 -0700

    Add openCypher labels() function AGE2-420
    
    Added the openCypher labels() function. This is Jira ticket AGE2-420.
    
    Added regression tests.
---
 age--0.6.0.sql                 |  8 +++++
 regress/expected/catalog.out   | 24 ++++++-------
 regress/expected/expr.out      | 53 +++++++++++++++++++++++++++-
 regress/sql/catalog.sql        |  4 +--
 regress/sql/expr.sql           | 11 +++++-
 src/backend/utils/adt/agtype.c | 78 +++++++++++++++++++++++++++++++++++++++---
 6 files changed, 158 insertions(+), 20 deletions(-)

diff --git a/age--0.6.0.sql b/age--0.6.0.sql
index e675a28..ea6e595 100644
--- a/age--0.6.0.sql
+++ b/age--0.6.0.sql
@@ -3748,6 +3748,14 @@ STRICT
 AS 'MODULE_PATHNAME';
 
 -- list functions
+CREATE FUNCTION ag_catalog.age_labels(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/catalog.out b/regress/expected/catalog.out
index dae8a0a..8a83f45 100644
--- a/regress/expected/catalog.out
+++ b/regress/expected/catalog.out
@@ -334,13 +334,13 @@ NOTICE:  ELabel "r" has been created
 (1 row)
 
 -- check if labels have been created or not
-SELECT * FROM ag_label;
-       name       | graph | id | kind |      relation      
-------------------+-------+----+------+--------------------
- _ag_label_vertex | 17629 |  1 | v    | g._ag_label_vertex
- _ag_label_edge   | 17629 |  2 | e    | g._ag_label_edge
- n                | 17629 |  3 | v    | g.n
- r                | 17629 |  4 | e    | g.r
+SELECT name, id, kind, relation FROM ag_label;
+       name       | id | kind |      relation      
+------------------+----+------+--------------------
+ _ag_label_vertex |  1 | v    | g._ag_label_vertex
+ _ag_label_edge   |  2 | e    | g._ag_label_edge
+ n                |  3 | v    | g.n
+ r                |  4 | e    | g.r
 (4 rows)
 
 -- try to create duplicate labels
@@ -364,11 +364,11 @@ NOTICE:  label "g"."r" has been dropped
 (1 row)
 
 -- check if labels have been deleted or not
-SELECT * FROM ag_label;
-       name       | graph | id | kind |      relation      
-------------------+-------+----+------+--------------------
- _ag_label_vertex | 17629 |  1 | v    | g._ag_label_vertex
- _ag_label_edge   | 17629 |  2 | e    | g._ag_label_edge
+SELECT name, id, kind, relation FROM ag_label;
+       name       | id | kind |      relation      
+------------------+----+------+--------------------
+ _ag_label_vertex |  1 | v    | g._ag_label_vertex
+ _ag_label_edge   |  2 | e    | g._ag_label_edge
 (2 rows)
 
 -- try to remove labels that is not there
diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index 1317aeb..c9e2155 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -5587,6 +5587,55 @@ SELECT * from cypher('list', $$RETURN range(0, null, -3)$$) as (range agtype);
 ERROR:  range(): neither start or end can be NULL
 SELECT * from cypher('list', $$RETURN range(0, -10.0, -3.0)$$) as (range agtype);
 ERROR:  range() unsupported argument type
+-- labels()
+SELECT * from cypher('list', $$CREATE (u:People {name: "John"}) RETURN u$$) as (Vertices agtype);
+                                      vertices                                       
+-------------------------------------------------------------------------------------
+ {"id": 1125899906842625, "label": "People", "properties": {"name": "John"}}::vertex
+(1 row)
+
+SELECT * from cypher('list', $$CREATE (u:People {name: "Larry"}) RETURN u$$) as (Vertices agtype);
+                                       vertices                                       
+--------------------------------------------------------------------------------------
+ {"id": 1125899906842626, "label": "People", "properties": {"name": "Larry"}}::vertex
+(1 row)
+
+SELECT * from cypher('list', $$CREATE (u:Cars {name: "G35"}) RETURN u$$) as (Vertices agtype);
+                                     vertices                                     
+----------------------------------------------------------------------------------
+ {"id": 1407374883553281, "label": "Cars", "properties": {"name": "G35"}}::vertex
+(1 row)
+
+SELECT * from cypher('list', $$CREATE (u:Cars {name: "MR2"}) RETURN u$$) as (Vertices agtype);
+                                     vertices                                     
+----------------------------------------------------------------------------------
+ {"id": 1407374883553282, "label": "Cars", "properties": {"name": "MR2"}}::vertex
+(1 row)
+
+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
+ ["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
+ ["Cars"]   | {"id": 1407374883553282, "label": "Cars", "properties": {"name": "MR2"}}::vertex
+(9 rows)
+
+-- should return SQL NULL
+SELECT * from cypher('list', $$RETURN labels(NULL)$$) as (Labels agtype);
+ labels 
+--------
+ 
+(1 row)
+
+-- should return an error
+SELECT * from cypher('list', $$RETURN labels("string")$$) as (Labels agtype);
+ERROR:  labels() argument must be a vertex
 --
 -- Cleanup
 --
@@ -5703,10 +5752,12 @@ NOTICE:  graph "keys" has been dropped
 (1 row)
 
 SELECT * FROM drop_graph('list', true);
-NOTICE:  drop cascades to 3 other objects
+NOTICE:  drop cascades to 5 other objects
 DETAIL:  drop cascades to table list._ag_label_vertex
 drop cascades to table list._ag_label_edge
 drop cascades to table list.knows
+drop cascades to table list."People"
+drop cascades to table list."Cars"
 NOTICE:  graph "list" has been dropped
  drop_graph 
 ------------
diff --git a/regress/sql/catalog.sql b/regress/sql/catalog.sql
index 37648a0..6bc1981 100644
--- a/regress/sql/catalog.sql
+++ b/regress/sql/catalog.sql
@@ -137,7 +137,7 @@ SELECT create_vlabel('g', 'n');
 SELECT create_elabel('g', 'r');
 
 -- check if labels have been created or not
-SELECT * FROM ag_label;
+SELECT name, id, kind, relation FROM ag_label;
 
 -- try to create duplicate labels
 SELECT create_vlabel('g', 'n');
@@ -148,7 +148,7 @@ SELECT drop_label('g', 'n', false);
 SELECT drop_label('g', 'r', false);
 
 -- check if labels have been deleted or not
-SELECT * FROM ag_label;
+SELECT name, id, kind, relation FROM ag_label;
 
 -- try to remove labels that is not there
 SELECT drop_label('g', 'n');
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index 3160a77..509e661 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -2266,7 +2266,16 @@ SELECT * from cypher('list', $$RETURN range(-10, 10, -1)$$) as (range agtype);
 SELECT * from cypher('list', $$RETURN range(null, -10, -3)$$) as (range agtype);
 SELECT * from cypher('list', $$RETURN range(0, null, -3)$$) as (range agtype);
 SELECT * from cypher('list', $$RETURN range(0, -10.0, -3.0)$$) as (range agtype);
-
+-- labels()
+SELECT * from cypher('list', $$CREATE (u:People {name: "John"}) RETURN u$$) as (Vertices agtype);
+SELECT * from cypher('list', $$CREATE (u:People {name: "Larry"}) RETURN u$$) as (Vertices agtype);
+SELECT * from cypher('list', $$CREATE (u:Cars {name: "G35"}) RETURN u$$) as (Vertices agtype);
+SELECT * from cypher('list', $$CREATE (u:Cars {name: "MR2"}) RETURN u$$) as (Vertices agtype);
+SELECT * from cypher('list', $$MATCH (u) RETURN labels(u), u$$) as (Labels agtype, Vertices agtype);
+-- should return SQL NULL
+SELECT * from cypher('list', $$RETURN labels(NULL)$$) as (Labels agtype);
+-- should return an error
+SELECT * from cypher('list', $$RETURN labels("string")$$) as (Labels agtype);
 --
 -- Cleanup
 --
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index 80c0cd8..b72cb53 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -8288,9 +8288,10 @@ Datum age_eq_tilde(PG_FUNCTION_ARGS)
                     errmsg("agtype string values expected")));
 }
 
-/* helper function to step through and retrieve keys from an object.
+/*
+ * Helper function to step through and retrieve keys from an object.
  * borrowed and modified from get_next_object_pair() in agtype_vle.c
-*/
+ */
 static agtype_iterator *get_next_object_key(agtype_iterator *it,
                                              agtype_container *agtc,
                                              agtype_value *key)
@@ -8418,7 +8419,75 @@ Datum age_keys(PG_FUNCTION_ARGS)
     Assert(agtv_result->type = AGTV_ARRAY);
 
     PG_RETURN_POINTER(agtype_value_to_agtype(agtv_result));
+}
+
+PG_FUNCTION_INFO_V1(age_labels);
+/*
+ * Execution function to implement openCypher labels() function
+ *
+ * NOTE:
+ *
+ * This function is defined to return NULL on NULL input. So, no need to check
+ * for SQL NULL input.
+ */
+Datum age_labels(PG_FUNCTION_ARGS)
+{
+    agtype *agt_arg = NULL;
+    agtype_value *agtv_temp = NULL;
+    agtype_value *agtv_label = NULL;
+    agtype_in_state agis_result;
+
+    /* get the vertex argument */
+    agt_arg = AG_GET_ARG_AGTYPE_P(0);
+
+    /* verify it is a scalar */
+    if (!AGT_ROOT_IS_SCALAR(agt_arg))
+    {
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("labels() argument must resolve to a scalar value")));
+    }
+
+    /* is it an agtype null? */
+    if (AGTYPE_CONTAINER_IS_SCALAR(&agt_arg->root) &&
+        AGTE_IS_NULL((&agt_arg->root)->children[0]))
+    {
+        PG_RETURN_NULL();
+    }
 
+    /* get the potential vertex */
+    agtv_temp = get_ith_agtype_value_from_container(&agt_arg->root, 0);
+
+    /* verify that it is an agtype vertex */
+    if (agtv_temp->type != AGTV_VERTEX)
+    {
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("labels() argument must be a vertex")));
+    }
+
+    /* get the label from the vertex */
+    agtv_label = get_agtype_value_object_value(agtv_temp, "label");
+    /* it cannot be NULL */
+    Assert(agtv_label != NULL);
+
+    /* 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 the label */
+    agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM,
+                                        agtv_label);
+
+    /* 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_relationships);
@@ -8442,8 +8511,9 @@ Datum age_relationships(PG_FUNCTION_ARGS)
     /* check for a scalar object */
     if (!AGT_ROOT_IS_SCALAR(agt_arg))
     {
-        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                        errmsg("relationships() argument must resolve to a scalar value")));
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("relationships() argument must resolve to a scalar value")));
     }
 
     /* get the potential path out of the array */