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);