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

dehowef pushed a commit to branch PG11
in repository https://gitbox.apache.org/repos/asf/age.git


The following commit(s) were added to refs/heads/PG11 by this push:
     new 5e5e9c61 Fix issue 1033: access operator on vle edge lists (#1037) 
(#1050)
5e5e9c61 is described below

commit 5e5e9c617ff1f590fe20887a84e46ce92d9b1648
Author: John Gemignani <[email protected]>
AuthorDate: Tue Jul 18 07:53:59 2023 -0700

    Fix issue 1033: access operator on vle edge lists (#1037) (#1050)
---
 regress/expected/cypher_vle.out | 186 ++++++++++++++++++++++++++++++++++++++++
 regress/sql/cypher_vle.sql      |  25 ++++++
 src/backend/utils/adt/agtype.c  | 127 ++++++++++++++-------------
 3 files changed, 278 insertions(+), 60 deletions(-)

diff --git a/regress/expected/cypher_vle.out b/regress/expected/cypher_vle.out
index e153ce97..71f520de 100644
--- a/regress/expected/cypher_vle.out
+++ b/regress/expected/cypher_vle.out
@@ -814,6 +814,192 @@ SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() 
MATCH p=() RETURN p $$)a
 ERROR:  variable "p" already exists
 LINE 1: ...M cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH p=() RETUR...
                                                              ^
+-- issue 1033, agtype_access_operator not working on containerized edges
+SELECT create_graph('access');
+NOTICE:  graph "access" has been created
+ create_graph 
+--------------
+ 
+(1 row)
+
+SELECT * FROM cypher('access',$$ CREATE ()-[:knows]->() $$) as (results 
agtype);
+ results 
+---------
+(0 rows)
+
+SELECT * FROM cypher('access',$$ CREATE ()-[:knows]->()-[:knows]->()$$) as 
(results agtype);
+ results 
+---------
+(0 rows)
+
+SELECT * FROM cypher('access',$$ CREATE ()-[:knows {id:0}]->()-[:knows {id: 
1}]->() $$) as (results agtype);
+ results 
+---------
+(0 rows)
+
+SELECT * FROM cypher('access',$$ CREATE ()-[:knows {id:2, arry:[0,1,2,3,{name: 
"joe"}]}]->()-[:knows {id: 3, arry:[1,3,{name:"john", stats: {age: 
1000}}]}]->() $$) as (results agtype);
+ results 
+---------
+(0 rows)
+
+SELECT * FROM cypher('access', $$ MATCH (u)-[e*]->(v) RETURN e $$)as (edges 
agtype);
+                                                                               
                                                                                
                  edges                                                         
                                                                                
                                        
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 844424930131969, "label": "knows", "end_id": 281474976710658, 
"start_id": 281474976710657, "properties": {}}::edge]
+ [{"id": 844424930131971, "label": "knows", "end_id": 281474976710660, 
"start_id": 281474976710659, "properties": {}}::edge]
+ [{"id": 844424930131971, "label": "knows", "end_id": 281474976710660, 
"start_id": 281474976710659, "properties": {}}::edge, {"id": 844424930131970, 
"label": "knows", "end_id": 281474976710661, "start_id": 281474976710660, 
"properties": {}}::edge]
+ [{"id": 844424930131970, "label": "knows", "end_id": 281474976710661, 
"start_id": 281474976710660, "properties": {}}::edge]
+ [{"id": 844424930131973, "label": "knows", "end_id": 281474976710663, 
"start_id": 281474976710662, "properties": {"id": 0}}::edge]
+ [{"id": 844424930131973, "label": "knows", "end_id": 281474976710663, 
"start_id": 281474976710662, "properties": {"id": 0}}::edge, {"id": 
844424930131972, "label": "knows", "end_id": 281474976710664, "start_id": 
281474976710663, "properties": {"id": 1}}::edge]
+ [{"id": 844424930131972, "label": "knows", "end_id": 281474976710664, 
"start_id": 281474976710663, "properties": {"id": 1}}::edge]
+ [{"id": 844424930131975, "label": "knows", "end_id": 281474976710666, 
"start_id": 281474976710665, "properties": {"id": 2, "arry": [0, 1, 2, 3, 
{"name": "joe"}]}}::edge]
+ [{"id": 844424930131975, "label": "knows", "end_id": 281474976710666, 
"start_id": 281474976710665, "properties": {"id": 2, "arry": [0, 1, 2, 3, 
{"name": "joe"}]}}::edge, {"id": 844424930131974, "label": "knows", "end_id": 
281474976710667, "start_id": 281474976710666, "properties": {"id": 3, "arry": 
[1, 3, {"name": "john", "stats": {"age": 1000}}]}}::edge]
+ [{"id": 844424930131974, "label": "knows", "end_id": 281474976710667, 
"start_id": 281474976710666, "properties": {"id": 3, "arry": [1, 3, {"name": 
"john", "stats": {"age": 1000}}]}}::edge]
+(10 rows)
+
+SELECT * FROM cypher('access', $$ MATCH (u)-[e*2..2]->(v) RETURN e $$)as 
(edges agtype);
+                                                                               
                                                                                
                  edges                                                         
                                                                                
                                        
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 844424930131971, "label": "knows", "end_id": 281474976710660, 
"start_id": 281474976710659, "properties": {}}::edge, {"id": 844424930131970, 
"label": "knows", "end_id": 281474976710661, "start_id": 281474976710660, 
"properties": {}}::edge]
+ [{"id": 844424930131973, "label": "knows", "end_id": 281474976710663, 
"start_id": 281474976710662, "properties": {"id": 0}}::edge, {"id": 
844424930131972, "label": "knows", "end_id": 281474976710664, "start_id": 
281474976710663, "properties": {"id": 1}}::edge]
+ [{"id": 844424930131975, "label": "knows", "end_id": 281474976710666, 
"start_id": 281474976710665, "properties": {"id": 2, "arry": [0, 1, 2, 3, 
{"name": "joe"}]}}::edge, {"id": 844424930131974, "label": "knows", "end_id": 
281474976710667, "start_id": 281474976710666, "properties": {"id": 3, "arry": 
[1, 3, {"name": "john", "stats": {"age": 1000}}]}}::edge]
+(3 rows)
+
+SELECT * FROM cypher('access',$$ MATCH ()-[e*2..2]->() RETURN properties(e[0]) 
$$) as (prop_first_edge agtype);
+                 prop_first_edge                  
+--------------------------------------------------
+ {}
+ {"id": 0}
+ {"id": 2, "arry": [0, 1, 2, 3, {"name": "joe"}]}
+(3 rows)
+
+SELECT * FROM cypher('access',$$ MATCH ()-[e*2..2]->() RETURN e[0].id $$) as 
(results agtype);
+ results 
+---------
+ 
+ 0
+ 2
+(3 rows)
+
+SELECT * FROM cypher('access',$$ MATCH ()-[e*2..2]->() RETURN e[0].arry[2] $$) 
as (results agtype);
+ results 
+---------
+ 
+ 
+ 2
+(3 rows)
+
+SELECT * FROM cypher('access',$$ MATCH ()-[e*2..2]->() RETURN properties(e[1]) 
$$) as (prop_second_edge agtype);
+                          prop_second_edge                           
+---------------------------------------------------------------------
+ {}
+ {"id": 1}
+ {"id": 3, "arry": [1, 3, {"name": "john", "stats": {"age": 1000}}]}
+(3 rows)
+
+SELECT * FROM cypher('access',$$ MATCH ()-[e*2..2]->() RETURN e[1].id $$) as 
(results agtype);
+ results 
+---------
+ 
+ 1
+ 3
+(3 rows)
+
+SELECT * FROM cypher('access',$$ MATCH ()-[e*2..2]->() RETURN e[1].arry[2] $$) 
as (results agtype);
+                 results                  
+------------------------------------------
+ 
+ 
+ {"name": "john", "stats": {"age": 1000}}
+(3 rows)
+
+SELECT * FROM cypher('access',$$ MATCH ()-[e*2..2]->() RETURN 
e[1].arry[2].stats $$) as (results agtype);
+    results    
+---------------
+ 
+ 
+ {"age": 1000}
+(3 rows)
+
+SELECT * FROM cypher('access',$$ MATCH ()-[e*2..2]->() RETURN properties(e[2]) 
$$) as (prop_third_edge agtype);
+ prop_third_edge 
+-----------------
+ 
+ 
+ 
+(3 rows)
+
+SELECT * FROM cypher('access',$$ MATCH ()-[e*]->() RETURN properties(e[0]), 
properties(e[1]) $$) as (prop_1st agtype, prop_2nd agtype);
+                              prop_1st                               |         
                     prop_2nd                               
+---------------------------------------------------------------------+---------------------------------------------------------------------
+ {}                                                                  | 
+ {}                                                                  | 
+ {}                                                                  | {}
+ {}                                                                  | 
+ {"id": 0}                                                           | 
+ {"id": 0}                                                           | {"id": 
1}
+ {"id": 1}                                                           | 
+ {"id": 2, "arry": [0, 1, 2, 3, {"name": "joe"}]}                    | 
+ {"id": 2, "arry": [0, 1, 2, 3, {"name": "joe"}]}                    | {"id": 
3, "arry": [1, 3, {"name": "john", "stats": {"age": 1000}}]}
+ {"id": 3, "arry": [1, 3, {"name": "john", "stats": {"age": 1000}}]} | 
+(10 rows)
+
+SELECT * FROM cypher('access',$$ MATCH ()-[e*]->() RETURN e[0].id, e[1].id $$) 
as (results_1st agtype, results_2nd agtype);
+ results_1st | results_2nd 
+-------------+-------------
+             | 
+             | 
+             | 
+             | 
+ 0           | 
+ 0           | 1
+ 1           | 
+ 2           | 
+ 2           | 3
+ 3           | 
+(10 rows)
+
+SELECT * FROM cypher('access',$$ MATCH ()-[e*]->() RETURN e[0].arry, e[1].arry 
$$) as (results_1st agtype, results_2nd agtype);
+                   results_1st                    |                   
results_2nd                    
+--------------------------------------------------+--------------------------------------------------
+                                                  | 
+                                                  | 
+                                                  | 
+                                                  | 
+                                                  | 
+                                                  | 
+                                                  | 
+ [0, 1, 2, 3, {"name": "joe"}]                    | 
+ [0, 1, 2, 3, {"name": "joe"}]                    | [1, 3, {"name": "john", 
"stats": {"age": 1000}}]
+ [1, 3, {"name": "john", "stats": {"age": 1000}}] | 
+(10 rows)
+
+SELECT * FROM cypher('access',$$ MATCH ()-[e*]->() RETURN e[0].arry[2], 
e[1].arry[2] $$) as (results_1st agtype, results_2nd agtype);
+               results_1st                |               results_2nd          
      
+------------------------------------------+------------------------------------------
+                                          | 
+                                          | 
+                                          | 
+                                          | 
+                                          | 
+                                          | 
+                                          | 
+ 2                                        | 
+ 2                                        | {"name": "john", "stats": {"age": 
1000}}
+ {"name": "john", "stats": {"age": 1000}} | 
+(10 rows)
+
+SELECT drop_graph('access', true);
+NOTICE:  drop cascades to 3 other objects
+DETAIL:  drop cascades to table access._ag_label_vertex
+drop cascades to table access._ag_label_edge
+drop cascades to table access.knows
+NOTICE:  graph "access" has been dropped
+ drop_graph 
+------------
+ 
+(1 row)
+
 --
 -- Clean up
 --
diff --git a/regress/sql/cypher_vle.sql b/regress/sql/cypher_vle.sql
index 13cf0594..33cf11da 100644
--- a/regress/sql/cypher_vle.sql
+++ b/regress/sql/cypher_vle.sql
@@ -305,6 +305,31 @@ SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() 
MATCH (p) RETURN p $$)as
 SELECT * FROM cypher('cypher_vle', $$ MATCH p=() MATCH ()-[p *]-() RETURN p 
$$)as (p agtype);
 SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH p=() RETURN p 
$$)as (p agtype);
 
+-- issue 1033, agtype_access_operator not working on containerized edges
+SELECT create_graph('access');
+
+SELECT * FROM cypher('access',$$ CREATE ()-[:knows]->() $$) as (results 
agtype);
+SELECT * FROM cypher('access',$$ CREATE ()-[:knows]->()-[:knows]->()$$) as 
(results agtype);
+SELECT * FROM cypher('access',$$ CREATE ()-[:knows {id:0}]->()-[:knows {id: 
1}]->() $$) as (results agtype);
+SELECT * FROM cypher('access',$$ CREATE ()-[:knows {id:2, arry:[0,1,2,3,{name: 
"joe"}]}]->()-[:knows {id: 3, arry:[1,3,{name:"john", stats: {age: 
1000}}]}]->() $$) as (results agtype);
+SELECT * FROM cypher('access', $$ MATCH (u)-[e*]->(v) RETURN e $$)as (edges 
agtype);
+SELECT * FROM cypher('access', $$ MATCH (u)-[e*2..2]->(v) RETURN e $$)as 
(edges agtype);
+SELECT * FROM cypher('access',$$ MATCH ()-[e*2..2]->() RETURN properties(e[0]) 
$$) as (prop_first_edge agtype);
+SELECT * FROM cypher('access',$$ MATCH ()-[e*2..2]->() RETURN e[0].id $$) as 
(results agtype);
+SELECT * FROM cypher('access',$$ MATCH ()-[e*2..2]->() RETURN e[0].arry[2] $$) 
as (results agtype);
+SELECT * FROM cypher('access',$$ MATCH ()-[e*2..2]->() RETURN properties(e[1]) 
$$) as (prop_second_edge agtype);
+SELECT * FROM cypher('access',$$ MATCH ()-[e*2..2]->() RETURN e[1].id $$) as 
(results agtype);
+SELECT * FROM cypher('access',$$ MATCH ()-[e*2..2]->() RETURN e[1].arry[2] $$) 
as (results agtype);
+SELECT * FROM cypher('access',$$ MATCH ()-[e*2..2]->() RETURN 
e[1].arry[2].stats $$) as (results agtype);
+SELECT * FROM cypher('access',$$ MATCH ()-[e*2..2]->() RETURN properties(e[2]) 
$$) as (prop_third_edge agtype);
+
+SELECT * FROM cypher('access',$$ MATCH ()-[e*]->() RETURN properties(e[0]), 
properties(e[1]) $$) as (prop_1st agtype, prop_2nd agtype);
+SELECT * FROM cypher('access',$$ MATCH ()-[e*]->() RETURN e[0].id, e[1].id $$) 
as (results_1st agtype, results_2nd agtype);
+SELECT * FROM cypher('access',$$ MATCH ()-[e*]->() RETURN e[0].arry, e[1].arry 
$$) as (results_1st agtype, results_2nd agtype);
+SELECT * FROM cypher('access',$$ MATCH ()-[e*]->() RETURN e[0].arry[2], 
e[1].arry[2] $$) as (results_1st agtype, results_2nd agtype);
+
+SELECT drop_graph('access', true);
+
 --
 -- Clean up
 --
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index 428d0519..6d0c5daf 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -3438,67 +3438,60 @@ Datum agtype_access_operator(PG_FUNCTION_ARGS)
     bool *nulls = NULL;
     Oid *types = NULL;
     int nargs = 0;
-    agtype *object = NULL;
-    agtype_value *object_value = NULL;
+    agtype *container = NULL;
+    agtype_value *container_value = NULL;
     int i = 0;
 
     /* extract our args, we need at least 2 */
     nargs = extract_variadic_args_min(fcinfo, 0, true, &args, &types, &nulls,
                                       2);
-    /* return NULL if we don't have the minimum number of args */
+    /*
+     * Return NULL if -
+     *
+     *     1) Our args are all null - nothing passed at all.
+     *     2) We don't have the minimum number of args. We require an object or
+     *        an array along with either a key or element number. Note that the
+     *        function extract_variadic_args_min will return 0 (nargs) if we
+     *        don't have at least 2 args.
+     *
+     */
     if (args == NULL || nargs == 0 || nulls[0] == true)
     {
         PG_RETURN_NULL();
     }
 
-    /* get the object argument */
-    object = DATUM_GET_AGTYPE_P(args[0]);
-
-    /* if the object is a scalar, it must be a vertex or edge */
-    if (AGT_ROOT_IS_SCALAR(object))
+    /* check for individual NULLs */
+    for (i = 0; i < nargs; i++)
     {
-        agtype_value *scalar_value = NULL;
-        agtype_value *property_value = NULL;
-
-        /* unpack the scalar */
-        scalar_value = get_ith_agtype_value_from_container(&object->root, 0);
-
-        /* get the properties depending on the type or fail */
-        if (scalar_value->type == AGTV_VERTEX)
-        {
-            property_value = &scalar_value->val.object.pairs[2].value;
-        }
-        else if (scalar_value->type == AGTV_EDGE)
-        {
-            property_value = &scalar_value->val.object.pairs[4].value;
-        }
-        else
-        {
-            ereport(ERROR,(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                           errmsg("scalar object must be a vertex or edge")));
-        }
-
-        /* if the properties are NULL, return NULL */
-        if (property_value == NULL || property_value->type == AGTV_NULL)
+        /* if we have a NULL, return NULL */
+        if (nulls[i] == true)
         {
             PG_RETURN_NULL();
         }
-
-        /* set the object_value to the property_value. */
-        object_value = property_value;
     }
 
-    /* check for NULL keys */
-    for (i = 1; i < nargs; i++)
+    /* get the container argument. It could be an object or array */
+    container = DATUM_GET_AGTYPE_P(args[0]);
+
+    /* if it is a scalar, open it and pull out the value */
+    if (AGT_ROOT_IS_SCALAR(container))
     {
-        /* if we have a NULL, return NULL */
-        if (nulls[i] == true)
+        container_value = get_ith_agtype_value_from_container(&container->root,
+                                                              0);
+
+        /* it must be either a vertex or an edge */
+        if (container_value->type != AGTV_EDGE &&
+            container_value->type != AGTV_VERTEX)
         {
-            PG_RETURN_NULL();
+                ereport(ERROR,(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                               errmsg("scalar object must be a vertex or 
edge")));
         }
+
+        /* clear the container reference */
+        container = NULL;
     }
 
-    /* iterate through the keys */
+    /* iterate through the keys (object fields or array elements) */
     for (i = 1; i < nargs; i++)
     {
         agtype *key = NULL;
@@ -3513,54 +3506,68 @@ Datum agtype_access_operator(PG_FUNCTION_ARGS)
                             errmsg("key must resolve to a scalar value")));
         }
 
+        /*
+         * Check for a vertex or edge container_value and extract the 
properties
+         * object.
+         */
+        if ((container_value != NULL &&
+             (container_value->type == AGTV_EDGE ||
+              container_value->type == AGTV_VERTEX)))
+        {
+            /* both are objects, get the properties object */
+            container_value = (container_value->type == AGTV_EDGE)
+                ? &container_value->val.object.pairs[4].value
+                : &container_value->val.object.pairs[2].value;
+        }
+
         /*
          * If we are dealing with a type of object, which can be an -
          * agtype OBJECT, an agtype_value OBJECT serialized (BINARY), or an
          * agtype_value OBJECT deserialized.
          */
-        if ((object_value != NULL &&
-             (object_value->type == AGTV_OBJECT ||
-             (object_value->type == AGTV_BINARY &&
-              AGTYPE_CONTAINER_IS_OBJECT(object_value->val.binary.data)))) ||
-            (object != NULL && AGT_ROOT_IS_OBJECT(object)))
+        if ((container_value != NULL &&
+             (container_value->type == AGTV_OBJECT ||
+              (container_value->type == AGTV_BINARY &&
+               AGTYPE_CONTAINER_IS_OBJECT(container_value->val.binary.data)))) 
||
+            (container != NULL && AGT_ROOT_IS_OBJECT(container)))
         {
-            object_value = execute_map_access_operator(object, object_value,
-                                                       key);
+            container_value = execute_map_access_operator(container,
+                                                          container_value, 
key);
         }
         /*
          * If we are dealing with a type of array, which can be an -
          * agtype ARRAY, an agtype_value ARRAY serialized (BINARY), or an
          * agtype_value ARRAY deserialized.
          */
-        else if ((object_value != NULL &&
-                  (object_value->type == AGTV_ARRAY ||
-                  (object_value->type == AGTV_BINARY &&
-                   AGTYPE_CONTAINER_IS_ARRAY(object_value->val.binary.data)))) 
||
-                 (object != NULL && AGT_ROOT_IS_ARRAY(object)))
+        else if ((container_value != NULL &&
+                  (container_value->type == AGTV_ARRAY ||
+                   (container_value->type == AGTV_BINARY &&
+                    
AGTYPE_CONTAINER_IS_ARRAY(container_value->val.binary.data)))) ||
+                 (container != NULL && AGT_ROOT_IS_ARRAY(container)))
         {
-            object_value = execute_array_access_operator(object, object_value,
-                                                         key);
+            container_value = execute_array_access_operator(container,
+                                                            container_value,
+                                                            key);
         }
-        /* this is unexpected */
         else
         {
+            /* this is unexpected */
             ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                             errmsg("container must be an array or object")));
         }
 
         /* for NULL values return NULL */
-        if (object_value == NULL || object_value->type == AGTV_NULL)
+        if (container_value == NULL || container_value->type == AGTV_NULL)
         {
             PG_RETURN_NULL();
         }
 
-        /* clear the object reference */
-        object = NULL;
-
+        /* clear the container reference */
+        container = NULL;
     }
 
     /* serialize and return the result */
-    return AGTYPE_P_GET_DATUM(agtype_value_to_agtype(object_value));
+    return AGTYPE_P_GET_DATUM(agtype_value_to_agtype(container_value));
 }
 
 PG_FUNCTION_INFO_V1(agtype_access_slice);

Reply via email to