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 */