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

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


The following commit(s) were added to refs/heads/PG14 by this push:
     new c87f0b8a Issue 1996 - Add agtype to json typecast (#2075) (#2076)
c87f0b8a is described below

commit c87f0b8afa107e7105a6861ae4fd348e7f27eba0
Author: Muhammad Taha Naveed <[email protected]>
AuthorDate: Mon Sep 9 23:07:02 2024 +0500

    Issue 1996 - Add agtype to json typecast (#2075) (#2076)
    
    - Added a helper function to determine agtype value type.
    - Added a function to cast agtype to json.
    - Added regression tests.
---
 age--1.5.0--y.y.y.sql               |  11 +++
 regress/expected/expr.out           | 179 ++++++++++++++++++++++++++++++++++++
 regress/sql/expr.sql                |  46 +++++++++
 sql/agtype_coercions.sql            |  11 +++
 src/backend/utils/adt/agtype.c      |  97 +++++++++++++++----
 src/backend/utils/adt/agtype_util.c |  82 +++++++++++++++++
 src/include/utils/agtype.h          |   3 +-
 7 files changed, 411 insertions(+), 18 deletions(-)

diff --git a/age--1.5.0--y.y.y.sql b/age--1.5.0--y.y.y.sql
index 710a196a..1d4aa602 100644
--- a/age--1.5.0--y.y.y.sql
+++ b/age--1.5.0--y.y.y.sql
@@ -146,3 +146,14 @@ CREATE OR REPLACE FUNCTION 
ag_catalog.create_elabel(graph_name cstring, label_na
     RETURNS void
     LANGUAGE c
     AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.agtype_to_json(agtype)
+    RETURNS json
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (agtype AS json)
+    WITH FUNCTION ag_catalog.agtype_to_json(agtype);
\ No newline at end of file
diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index 4094fbb3..a476b5ec 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -2426,6 +2426,185 @@ SELECT agtype_typecast_path(null);
  
 (1 row)
 
+--
+-- Tests for explicit typecast to json
+--
+-- Should pass
+SELECT agtype_to_json('{}'::agtype);
+ agtype_to_json 
+----------------
+ {}
+(1 row)
+
+SELECT agtype_to_json('{ "hello": "world" }'::agtype);
+   agtype_to_json   
+--------------------
+ {"hello": "world"}
+(1 row)
+
+SELECT agtype_to_json('{ "hello": "world" }'::agtype)->>'hello';
+ ?column? 
+----------
+ world
+(1 row)
+
+SELECT agtype_to_json('[]'::agtype);
+ agtype_to_json 
+----------------
+ []
+(1 row)
+
+SELECT agtype_to_json('[1, 2, 3]'::agtype);
+ agtype_to_json 
+----------------
+ [1, 2, 3]
+(1 row)
+
+SELECT agtype_to_json(null::agtype);
+ agtype_to_json 
+----------------
+ 
+(1 row)
+
+SELECT cast('{}'::agtype as json);
+ json 
+------
+ {}
+(1 row)
+
+SELECT cast('{ "hello": "world" }'::agtype as json);
+        json        
+--------------------
+ {"hello": "world"}
+(1 row)
+
+SELECT cast('{ "hello": "world" }'::agtype as json)->>'hello';
+ ?column? 
+----------
+ world
+(1 row)
+
+SELECT cast('[]'::agtype as json);
+ json 
+------
+ []
+(1 row)
+
+SELECT cast('[1, 2, 3]'::agtype as json);
+   json    
+-----------
+ [1, 2, 3]
+(1 row)
+
+SELECT cast('[1, 2, 3]'::agtype as json)->1;
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT cast(null::agtype as json);
+ json 
+------
+ 
+(1 row)
+
+SELECT vertex_in_json, vertex_in_json->'id' as id, pg_typeof(vertex_in_json) 
FROM cypher('type_coercion', $$ MATCH (a) RETURN a $$) AS (vertex_in_json json);
+                     vertex_in_json                     |       id        | 
pg_typeof 
+--------------------------------------------------------+-----------------+-----------
+ {"id": 281474976710657, "label": "", "properties": {}} | 281474976710657 | 
json
+ {"id": 281474976710658, "label": "", "properties": {}} | 281474976710658 | 
json
+(2 rows)
+
+SELECT edge_in_json, edge_in_json->'id' as id, pg_typeof(edge_in_json) FROM 
cypher('type_coercion', $$ MATCH ()-[e]->() RETURN e $$) AS (edge_in_json json);
+                                                    edge_in_json               
                                     |       id        | pg_typeof 
+--------------------------------------------------------------------------------------------------------------------+-----------------+-----------
+ {"id": 844424930131969, "label": "edge", "end_id": 281474976710658, 
"start_id": 281474976710657, "properties": {}} | 844424930131969 | json
+(1 row)
+
+SELECT vle_in_json, vle_in_json->0 as first_edge, pg_typeof(vle_in_json) FROM 
cypher('type_coercion', $$ MATCH ()-[e *]->() RETURN e $$) AS (vle_in_json 
json);
+                                                     vle_in_json               
                                       |                                        
             first_edge                                                     | 
pg_typeof 
+----------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------+-----------
+ [{"id": 844424930131969, "label": "edge", "end_id": 281474976710658, 
"start_id": 281474976710657, "properties": {}}] | {"id": 844424930131969, 
"label": "edge", "end_id": 281474976710658, "start_id": 281474976710657, 
"properties": {}} | json
+(1 row)
+
+SELECT *, pg_typeof(props_in_json) FROM cypher('type_coercion', $$ MATCH (a) 
RETURN properties(a) $$) AS (props_in_json json);
+ props_in_json | pg_typeof 
+---------------+-----------
+ {}            | json
+ {}            | json
+(2 rows)
+
+SELECT path_in_json, path_in_json->0 as first_node FROM 
cypher('type_coercion', $$ MATCH p=()-[]->() RETURN p $$) AS (path_in_json 
json);
+                                                                               
                              path_in_json                                      
                                                                       |        
               first_node                       
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------
+ [{"id": 281474976710657, "label": "", "properties": {}}, {"id": 
844424930131969, "label": "edge", "end_id": 281474976710658, "start_id": 
281474976710657, "properties": {}}, {"id": 281474976710658, "label": "", 
"properties": {}}] | {"id": 281474976710657, "label": "", "properties": {}}
+(1 row)
+
+SELECT *, pg_typeof(nodes_in_json) FROM cypher('type_coercion', $$ MATCH 
p=()-[]->() RETURN nodes(p) $$) AS (nodes_in_json json);
+                                                  nodes_in_json                
                                   | pg_typeof 
+------------------------------------------------------------------------------------------------------------------+-----------
+ [{"id": 281474976710657, "label": "", "properties": {}}, {"id": 
281474976710658, "label": "", "properties": {}}] | json
+(1 row)
+
+SELECT *, pg_typeof(rels_in_json) FROM cypher('type_coercion', $$ MATCH 
p=()-[]->() RETURN relationships(p) $$) AS (rels_in_json json);
+                                                     rels_in_json              
                                       | pg_typeof 
+----------------------------------------------------------------------------------------------------------------------+-----------
+ [{"id": 844424930131969, "label": "edge", "end_id": 281474976710658, 
"start_id": 281474976710657, "properties": {}}] | json
+(1 row)
+
+SELECT cast(result as json) FROM cypher('type_coercion', $$ MATCH (a) RETURN a 
$$) AS (result agtype);
+                         result                         
+--------------------------------------------------------
+ {"id": 281474976710657, "label": "", "properties": {}}
+ {"id": 281474976710658, "label": "", "properties": {}}
+(2 rows)
+
+SELECT cast(result as json) FROM cypher('type_coercion', $$ MATCH ()-[e]-() 
RETURN e $$) AS (result agtype);
+                                                       result                  
                                     
+--------------------------------------------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "edge", "end_id": 281474976710658, 
"start_id": 281474976710657, "properties": {}}
+ {"id": 844424930131969, "label": "edge", "end_id": 281474976710658, 
"start_id": 281474976710657, "properties": {}}
+(2 rows)
+
+SELECT cast(result as json) FROM cypher('type_coercion', $$ MATCH ()-[e *]->() 
RETURN e $$) AS (result agtype);
+                                                        result                 
                                       
+----------------------------------------------------------------------------------------------------------------------
+ [{"id": 844424930131969, "label": "edge", "end_id": 281474976710658, 
"start_id": 281474976710657, "properties": {}}]
+(1 row)
+
+SELECT cast(result as json) FROM cypher('type_coercion', $$ MATCH p=()-[]->() 
RETURN p $$) AS (result agtype);
+                                                                               
                                 result                                         
                                                                       
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710657, "label": "", "properties": {}}, {"id": 
844424930131969, "label": "edge", "end_id": 281474976710658, "start_id": 
281474976710657, "properties": {}}, {"id": 281474976710658, "label": "", 
"properties": {}}]
+(1 row)
+
+SELECT pg_typeof(cast(result as json)) FROM cypher('type_coercion', $$ MATCH 
p=()-[]->() RETURN p $$) AS (result agtype);
+ pg_typeof 
+-----------
+ json
+(1 row)
+
+-- Should fail
+SELECT agtype_to_json('1'::agtype);
+ERROR:  cannot cast agtype integer to json
+SELECT agtype_to_json('1.111'::agtype);
+ERROR:  cannot cast agtype float to json
+SELECT agtype_to_json('true'::agtype);
+ERROR:  cannot cast agtype boolean to json
+SELECT agtype_to_json('false'::agtype);
+ERROR:  cannot cast agtype boolean to json
+SELECT agtype_to_json('1::numeric'::agtype);
+ERROR:  cannot cast agtype numeric to json
+SELECT cast(result as json) FROM cypher('type_coercion', $$ RETURN 1 $$) AS 
(result agtype);
+ERROR:  cannot cast agtype integer to json
+SELECT cast(result as json) FROM cypher('type_coercion', $$ RETURN 1.111 $$) 
AS (result agtype);
+ERROR:  cannot cast agtype float to json
+SELECT cast(result as json) FROM cypher('type_coercion', $$ RETURN true $$) AS 
(result agtype);
+ERROR:  cannot cast agtype boolean to json
+SELECT cast(result as json) FROM cypher('type_coercion', $$ RETURN false $$) 
AS (result agtype);
+ERROR:  cannot cast agtype boolean to json
+SELECT cast(result as json) FROM cypher('type_coercion', $$ RETURN 1::numeric 
$$) AS (result agtype);
+ERROR:  cannot cast agtype numeric to json
 -- test functions
 -- create some vertices and edges
 SELECT * FROM cypher('expr', $$CREATE (:v)$$) AS (a agtype);
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index 27a202b4..7aae42c7 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -1065,6 +1065,52 @@ SELECT agtype_in('null::path');
 SELECT * FROM cypher('expr', $$ RETURN null::path $$) AS r(result agtype);
 SELECT agtype_typecast_path(agtype_in('null'));
 SELECT agtype_typecast_path(null);
+--
+-- Tests for explicit typecast to json
+--
+
+-- Should pass
+SELECT agtype_to_json('{}'::agtype);
+SELECT agtype_to_json('{ "hello": "world" }'::agtype);
+SELECT agtype_to_json('{ "hello": "world" }'::agtype)->>'hello';
+SELECT agtype_to_json('[]'::agtype);
+SELECT agtype_to_json('[1, 2, 3]'::agtype);
+SELECT agtype_to_json(null::agtype);
+
+SELECT cast('{}'::agtype as json);
+SELECT cast('{ "hello": "world" }'::agtype as json);
+SELECT cast('{ "hello": "world" }'::agtype as json)->>'hello';
+SELECT cast('[]'::agtype as json);
+SELECT cast('[1, 2, 3]'::agtype as json);
+SELECT cast('[1, 2, 3]'::agtype as json)->1;
+SELECT cast(null::agtype as json);
+
+SELECT vertex_in_json, vertex_in_json->'id' as id, pg_typeof(vertex_in_json) 
FROM cypher('type_coercion', $$ MATCH (a) RETURN a $$) AS (vertex_in_json json);
+SELECT edge_in_json, edge_in_json->'id' as id, pg_typeof(edge_in_json) FROM 
cypher('type_coercion', $$ MATCH ()-[e]->() RETURN e $$) AS (edge_in_json json);
+SELECT vle_in_json, vle_in_json->0 as first_edge, pg_typeof(vle_in_json) FROM 
cypher('type_coercion', $$ MATCH ()-[e *]->() RETURN e $$) AS (vle_in_json 
json);
+SELECT *, pg_typeof(props_in_json) FROM cypher('type_coercion', $$ MATCH (a) 
RETURN properties(a) $$) AS (props_in_json json);
+SELECT path_in_json, path_in_json->0 as first_node FROM 
cypher('type_coercion', $$ MATCH p=()-[]->() RETURN p $$) AS (path_in_json 
json);
+SELECT *, pg_typeof(nodes_in_json) FROM cypher('type_coercion', $$ MATCH 
p=()-[]->() RETURN nodes(p) $$) AS (nodes_in_json json);
+SELECT *, pg_typeof(rels_in_json) FROM cypher('type_coercion', $$ MATCH 
p=()-[]->() RETURN relationships(p) $$) AS (rels_in_json json);
+
+SELECT cast(result as json) FROM cypher('type_coercion', $$ MATCH (a) RETURN a 
$$) AS (result agtype);
+SELECT cast(result as json) FROM cypher('type_coercion', $$ MATCH ()-[e]-() 
RETURN e $$) AS (result agtype);
+SELECT cast(result as json) FROM cypher('type_coercion', $$ MATCH ()-[e *]->() 
RETURN e $$) AS (result agtype);
+SELECT cast(result as json) FROM cypher('type_coercion', $$ MATCH p=()-[]->() 
RETURN p $$) AS (result agtype);
+SELECT pg_typeof(cast(result as json)) FROM cypher('type_coercion', $$ MATCH 
p=()-[]->() RETURN p $$) AS (result agtype);
+
+-- Should fail
+SELECT agtype_to_json('1'::agtype);
+SELECT agtype_to_json('1.111'::agtype);
+SELECT agtype_to_json('true'::agtype);
+SELECT agtype_to_json('false'::agtype);
+SELECT agtype_to_json('1::numeric'::agtype);
+
+SELECT cast(result as json) FROM cypher('type_coercion', $$ RETURN 1 $$) AS 
(result agtype);
+SELECT cast(result as json) FROM cypher('type_coercion', $$ RETURN 1.111 $$) 
AS (result agtype);
+SELECT cast(result as json) FROM cypher('type_coercion', $$ RETURN true $$) AS 
(result agtype);
+SELECT cast(result as json) FROM cypher('type_coercion', $$ RETURN false $$) 
AS (result agtype);
+SELECT cast(result as json) FROM cypher('type_coercion', $$ RETURN 1::numeric 
$$) AS (result agtype);
 
 -- test functions
 -- create some vertices and edges
diff --git a/sql/agtype_coercions.sql b/sql/agtype_coercions.sql
index cdf5f6f8..745190fd 100644
--- a/sql/agtype_coercions.sql
+++ b/sql/agtype_coercions.sql
@@ -141,3 +141,14 @@ AS 'MODULE_PATHNAME';
 
 CREATE CAST (agtype AS int[])
     WITH FUNCTION ag_catalog.agtype_to_int4_array(variadic "any");
+
+CREATE FUNCTION ag_catalog.agtype_to_json(agtype)
+    RETURNS json
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (agtype AS json)
+    WITH FUNCTION ag_catalog.agtype_to_json(agtype);
\ No newline at end of file
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index 56c70217..4d37c6e0 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -29,6 +29,7 @@
  */
 
 #include "postgres.h"
+#include "utils/jsonfuncs.h"
 
 #include <float.h>
 
@@ -96,7 +97,8 @@ static void agtype_in_array_start(void *pstate);
 static void agtype_in_array_end(void *pstate);
 static void agtype_in_object_field_start(void *pstate, char *fname,
                                          bool isnull);
-static void agtype_put_escaped_value(StringInfo out, agtype_value *scalar_val);
+static void agtype_put_escaped_value(StringInfo out, agtype_value *scalar_val,
+                                     bool extend);
 static void escape_agtype(StringInfo buf, const char *str);
 bool is_decimal_needed(char *numstr);
 static void agtype_in_scalar(void *pstate, char *token,
@@ -114,7 +116,8 @@ static void datum_to_agtype(Datum val, bool is_null, 
agtype_in_state *result,
                             agt_type_category tcategory, Oid outfuncoid,
                             bool key_scalar);
 static char *agtype_to_cstring_worker(StringInfo out, agtype_container *in,
-                                      int estimated_len, bool indent);
+                                      int estimated_len, bool indent,
+                                      bool extend);
 static text *agtype_value_to_text(agtype_value *scalar_val,
                                   bool err_not_scalar);
 static void add_indent(StringInfo out, bool indent, int level);
@@ -808,7 +811,8 @@ static bool is_array_path(agtype_value *agtv)
     return true;
 }
 
-static void agtype_put_escaped_value(StringInfo out, agtype_value *scalar_val)
+static void agtype_put_escaped_value(StringInfo out, agtype_value *scalar_val,
+                                     bool extend)
 {
     char *numstr;
 
@@ -825,7 +829,10 @@ static void agtype_put_escaped_value(StringInfo out, 
agtype_value *scalar_val)
         appendStringInfoString(
             out, DatumGetCString(DirectFunctionCall1(
                      numeric_out, PointerGetDatum(scalar_val->val.numeric))));
-        appendBinaryStringInfo(out, "::numeric", 9);
+        if (extend)
+        {
+            appendBinaryStringInfo(out, "::numeric", 9);
+        }
         break;
     case AGTV_INTEGER:
         appendStringInfoString(
@@ -851,8 +858,12 @@ static void agtype_put_escaped_value(StringInfo out, 
agtype_value *scalar_val)
         agtype *prop;
         scalar_val->type = AGTV_OBJECT;
         prop = agtype_value_to_agtype(scalar_val);
-        agtype_to_cstring_worker(out, &prop->root, prop->vl_len_, false);
-        appendBinaryStringInfo(out, "::vertex", 8);
+        agtype_to_cstring_worker(out, &prop->root, prop->vl_len_,
+                                 false, extend);
+        if (extend)
+        {
+            appendBinaryStringInfo(out, "::vertex", 8);
+        }
         break;
     }
     case AGTV_EDGE:
@@ -860,8 +871,12 @@ static void agtype_put_escaped_value(StringInfo out, 
agtype_value *scalar_val)
         agtype *prop;
         scalar_val->type = AGTV_OBJECT;
         prop = agtype_value_to_agtype(scalar_val);
-        agtype_to_cstring_worker(out, &prop->root, prop->vl_len_, false);
-        appendBinaryStringInfo(out, "::edge", 6);
+        agtype_to_cstring_worker(out, &prop->root, prop->vl_len_,
+                                 false, extend);
+        if (extend)
+        {
+            appendBinaryStringInfo(out, "::edge", 6);
+        }
         break;
     }
     case AGTV_PATH:
@@ -869,8 +884,12 @@ static void agtype_put_escaped_value(StringInfo out, 
agtype_value *scalar_val)
         agtype *prop;
         scalar_val->type = AGTV_ARRAY;
         prop = agtype_value_to_agtype(scalar_val);
-        agtype_to_cstring_worker(out, &prop->root, prop->vl_len_, false);
-        appendBinaryStringInfo(out, "::path", 6);
+        agtype_to_cstring_worker(out, &prop->root, prop->vl_len_,
+                                 false, extend);
+        if (extend)
+        {
+            appendBinaryStringInfo(out, "::path", 6);
+        }
         break;
     }
 
@@ -1068,7 +1087,8 @@ static void agtype_in_scalar(void *pstate, char *token,
 char *agtype_to_cstring(StringInfo out, agtype_container *in,
                         int estimated_len)
 {
-    return agtype_to_cstring_worker(out, in, estimated_len, false);
+    return agtype_to_cstring_worker(out, in, estimated_len, false,
+                                    true);
 }
 
 /*
@@ -1077,14 +1097,16 @@ char *agtype_to_cstring(StringInfo out, 
agtype_container *in,
 char *agtype_to_cstring_indent(StringInfo out, agtype_container *in,
                                int estimated_len)
 {
-    return agtype_to_cstring_worker(out, in, estimated_len, true);
+    return agtype_to_cstring_worker(out, in, estimated_len, true,
+                                    true);
 }
 
 /*
  * common worker for above two functions
  */
 static char *agtype_to_cstring_worker(StringInfo out, agtype_container *in,
-                                      int estimated_len, bool indent)
+                                      int estimated_len, bool indent,
+                                      bool extend)
 {
     bool first = true;
     agtype_iterator *it;
@@ -1152,14 +1174,14 @@ static char *agtype_to_cstring_worker(StringInfo out, 
agtype_container *in,
             add_indent(out, use_indent, level);
 
             /* agtype rules guarantee this is a string */
-            agtype_put_escaped_value(out, &v);
+            agtype_put_escaped_value(out, &v, extend);
             appendBinaryStringInfo(out, ": ", 2);
 
             type = agtype_iterator_next(&it, &v, false);
             if (type == WAGT_VALUE)
             {
                 first = false;
-                agtype_put_escaped_value(out, &v);
+                agtype_put_escaped_value(out, &v, extend);
             }
             else
             {
@@ -1180,7 +1202,7 @@ static char *agtype_to_cstring_worker(StringInfo out, 
agtype_container *in,
 
             if (!raw_scalar)
                 add_indent(out, use_indent, level);
-            agtype_put_escaped_value(out, &v);
+            agtype_put_escaped_value(out, &v, extend);
             break;
         case WAGT_END_ARRAY:
             level--;
@@ -3178,6 +3200,47 @@ Datum agtype_to_text(PG_FUNCTION_ARGS)
     PG_RETURN_TEXT_P(text_value);
 }
 
+PG_FUNCTION_INFO_V1(agtype_to_json);
+
+/*
+ * Cast agtype to json.
+ *
+ * If the input agtype is vertex, edge or path, the trailing
+ * type(::vertex, ::edge, ::path) is removed.
+ */
+Datum agtype_to_json(PG_FUNCTION_ARGS)
+{
+    Datum result;
+    char *json_str;
+    agtype *agt;
+
+    agt = AG_GET_ARG_AGTYPE_P(0);
+
+    if (AGT_ROOT_IS_SCALAR(agt))
+    {
+        enum agtype_value_type type;
+
+        type = get_ith_agtype_value_type(&agt->root, 0);
+        if (type >= AGTV_NUMERIC && type <= AGTV_BOOL)
+        {
+            ereport(ERROR,
+                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                     errmsg("cannot cast agtype %s to json",
+                             agtype_value_type_to_string(type))));
+        }
+    }
+
+    json_str = agtype_to_cstring_worker(NULL, &agt->root, VARSIZE(agt),
+                                        false, false);
+
+    result = DirectFunctionCall1(json_in, CStringGetDatum(json_str));
+
+    PG_FREE_IF_COPY(agt, 0);
+    pfree(json_str);
+
+    PG_RETURN_DATUM(result);
+}
+
 PG_FUNCTION_INFO_V1(bool_to_agtype);
 
 /*
@@ -3702,7 +3765,7 @@ static Datum 
process_access_operator_result(FunctionCallInfo fcinfo,
 
                 str = agtype_to_cstring_worker(out, agtc,
                                                agtv->val.binary.len,
-                                               false);
+                                               false, true);
                 result = cstring_to_text(str);
             }
             else
diff --git a/src/backend/utils/adt/agtype_util.c 
b/src/backend/utils/adt/agtype_util.c
index 5ba6e727..878f1844 100644
--- a/src/backend/utils/adt/agtype_util.c
+++ b/src/backend/utils/adt/agtype_util.c
@@ -590,6 +590,88 @@ agtype_value 
*get_ith_agtype_value_from_container(agtype_container *container,
     return result;
 }
 
+/*
+ * Get type of i-th value of an agtype array.
+ */
+enum agtype_value_type get_ith_agtype_value_type(agtype_container *container,
+                                                 uint32 i)
+{
+    enum agtype_value_type type;
+    uint32 nelements;
+    agtentry entry;
+
+    if (!AGTYPE_CONTAINER_IS_ARRAY(container))
+    {
+        ereport(ERROR, (errmsg("container is not an agtype array")));
+    }
+
+    nelements = AGTYPE_CONTAINER_SIZE(container);
+    if (i >= nelements)
+    {
+        ereport(ERROR, (errmsg("index out of bounds")));
+    }
+
+    entry = container->children[i];
+    switch ((entry)&AGTENTRY_TYPEMASK)
+    {
+    case AGTENTRY_IS_STRING:
+        type = AGTV_STRING;
+        break;
+    case AGTENTRY_IS_NUMERIC:
+        type = AGTV_NUMERIC;
+        break;
+    case AGTENTRY_IS_AGTYPE:
+    {
+        char *base_addr;
+        uint32 agt_header;
+        char *base;
+
+        base_addr = (char *)&container->children[nelements];
+        base = base_addr + INTALIGN(get_agtype_offset(container, i));
+        agt_header = *((uint32 *)base);
+
+        switch (agt_header)
+        {
+        case AGT_HEADER_INTEGER:
+            type = AGTV_INTEGER;
+            break;
+        case AGT_HEADER_FLOAT:
+            type = AGTV_FLOAT;
+            break;
+        case AGT_HEADER_VERTEX:
+            type = AGTV_VERTEX;
+            break;
+        case AGT_HEADER_EDGE:
+            type = AGTV_EDGE;
+            break;
+        case AGT_HEADER_PATH:
+            type = AGTV_PATH;
+            break;
+        default:
+            ereport(ERROR, (errmsg("unexpected agt_header type")));
+            break;
+        }
+        break;
+    }
+    case AGTENTRY_IS_BOOL_TRUE:
+        type = AGTV_BOOL;
+        break;
+    case AGTENTRY_IS_BOOL_FALSE:
+        type = AGTV_BOOL;
+        break;
+    case AGTENTRY_IS_NULL:
+        type = AGTV_NULL;
+        break;
+    case AGTENTRY_IS_CONTAINER:
+        type = AGTV_BINARY;
+        break;
+    default:
+        ereport(ERROR, (errmsg("unexpected agtentry type")));
+        break;
+    }
+    return type;
+}
+
 /*
  * A helper function to fill in an agtype_value to represent an element of an
  * array, or a key or value of an object.
diff --git a/src/include/utils/agtype.h b/src/include/utils/agtype.h
index 96b2300e..a1e14b32 100644
--- a/src/include/utils/agtype.h
+++ b/src/include/utils/agtype.h
@@ -467,6 +467,8 @@ agtype_value 
*find_agtype_value_from_container(agtype_container *container,
                                                agtype_value *key);
 agtype_value *get_ith_agtype_value_from_container(agtype_container *container,
                                                   uint32 i);
+enum agtype_value_type get_ith_agtype_value_type(agtype_container *container,
+                                                 uint32 i);
 agtype_value *push_agtype_value(agtype_parse_state **pstate,
                                 agtype_iterator_token seq,
                                 agtype_value *agtval);
@@ -555,7 +557,6 @@ void pfree_agtype_value(agtype_value* value);
 void pfree_agtype_value_content(agtype_value* value);
 void pfree_agtype_in_state(agtype_in_state* value);
 agtype_value *agtype_value_from_cstring(char *str, int len);
-
 /* Oid accessors for AGTYPE */
 Oid get_AGTYPEOID(void);
 Oid get_AGTYPEARRAYOID(void);

Reply via email to