Skip to content

Commit

Permalink
Issue 1996 - Add agtype to json typecast (#2075)
Browse files Browse the repository at this point in the history
- Added a helper function to determine agtype value type.
- Added a function to cast agtype to json.
- Added regression tests.
  • Loading branch information
MuhammadTahaNaveed committed Aug 26, 2024
1 parent 1a88e86 commit a9ee434
Show file tree
Hide file tree
Showing 7 changed files with 411 additions and 19 deletions.
11 changes: 11 additions & 0 deletions age--1.5.0--y.y.y.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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);
179 changes: 179 additions & 0 deletions regress/expected/expr.out
Original file line number Diff line number Diff line change
Expand Up @@ -2418,6 +2418,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);
Expand Down
46 changes: 46 additions & 0 deletions regress/sql/expr.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions sql/agtype_coercions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Loading

0 comments on commit a9ee434

Please sign in to comment.