You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@age.apache.org by jo...@apache.org on 2022/04/21 17:14:47 UTC
[incubator-age] branch master updated: Allow lists and maps to be used in the SET clause
This is an automated email from the ASF dual-hosted git repository.
joshinnis 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 ab70781 Allow lists and maps to be used in the SET clause
ab70781 is described below
commit ab70781b4b311922f14f2bcb1b2144f1df9a9da1
Author: Josh Innis <Jo...@gmail.com>
AuthorDate: Thu Apr 21 10:13:28 2022 -0700
Allow lists and maps to be used in the SET clause
Patch fixes a bug that did not allow lists or maps
to be used in the SET clause properly.
Optimized some code in the SET clause.
Code cleanup and documenation added too.
---
regress/expected/cypher_set.out | 176 +++++++++++++++++++++++++++++++++++++-
regress/sql/cypher_set.sql | 33 ++++++-
src/backend/executor/cypher_set.c | 75 +++++++++++++++-
src/backend/utils/adt/agtype.c | 113 +++++++++++++++++++++---
4 files changed, 380 insertions(+), 17 deletions(-)
diff --git a/regress/expected/cypher_set.out b/regress/expected/cypher_set.out
index 45e9ff7..5cd42ee 100644
--- a/regress/expected/cypher_set.out
+++ b/regress/expected/cypher_set.out
@@ -493,6 +493,180 @@ SELECT * FROM cypher('cypher_set', $$MATCH (u:begin)-[:edge]->(v:end) return u,
{"id": 1970324836974593, "label": "begin", "properties": {"i": 2, "j": 4}}::vertex | {"id": 2533274790395905, "label": "end", "properties": {"i": 1, "j": 3}}::vertex
(1 row)
+-- test lists
+SELECT * FROM cypher('cypher_set', $$MATCH (n) SET n.i = [3, 'test', [1, 2, 3], {id: 1}, 1.0, 1.0::numeric] RETURN n$$) AS (a agtype);
+ a
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "y": 2}}::vertex
+ {"id": 281474976710658, "label": "", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "t": 150, "y": 1}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "t": 150}}::vertex
+ {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
+ {"id": 844424930131969, "label": "v", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "t": 150}}::vertex
+ {"id": 844424930131971, "label": "v", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "t": 150}}::vertex
+ {"id": 1407374883553281, "label": "other_v", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553282, "label": "other_v", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553283, "label": "other_v", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553284, "label": "other_v", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "k": 10}}::vertex
+ {"id": 1688849860263937, "label": "vertices", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 6, "k": 9}}::vertex
+ {"id": 1970324836974593, "label": "begin", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 4}}::vertex
+ {"id": 2533274790395905, "label": "end", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 3}}::vertex
+(13 rows)
+
+SELECT * FROM cypher('cypher_set', $$MATCH (n) RETURN n$$) AS (a agtype);
+ a
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "y": 2}}::vertex
+ {"id": 281474976710658, "label": "", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "t": 150, "y": 1}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "t": 150}}::vertex
+ {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
+ {"id": 844424930131969, "label": "v", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "t": 150}}::vertex
+ {"id": 844424930131971, "label": "v", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "t": 150}}::vertex
+ {"id": 1407374883553281, "label": "other_v", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553282, "label": "other_v", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553283, "label": "other_v", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553284, "label": "other_v", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 5, "k": 10}}::vertex
+ {"id": 1688849860263937, "label": "vertices", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 6, "k": 9}}::vertex
+ {"id": 1970324836974593, "label": "begin", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 4}}::vertex
+ {"id": 2533274790395905, "label": "end", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 3}}::vertex
+(13 rows)
+
+-- test that lists get updated in paths
+SELECT * FROM cypher('cypher_set', $$MATCH p=(u:begin)-[:edge]->(v:end) SET u.i = [1, 2, 3] return u, p $$) AS (u agtype, p agtype);
+ u | p
+--------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 1970324836974593, "label": "begin", "properties": {"i": [1, 2, 3], "j": 4}}::vertex | [{"id": 1970324836974593, "label": "begin", "properties": {"i": [1, 2, 3], "j": 4}}::vertex, {"id": 2251799813685249, "label": "edge", "end_id": 2533274790395905, "start_id": 1970324836974593, "properties": {}}::edge, {"id": 2533274790395905, "label": "end", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 3}}::vertex]::path
+(1 row)
+
+SELECT * FROM cypher('cypher_set', $$MATCH p=(u:begin)-[:edge]->(v:end) return u, p $$) AS (u agtype, p agtype);
+ u | p
+--------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 1970324836974593, "label": "begin", "properties": {"i": [1, 2, 3], "j": 4}}::vertex | [{"id": 1970324836974593, "label": "begin", "properties": {"i": [1, 2, 3], "j": 4}}::vertex, {"id": 2251799813685249, "label": "edge", "end_id": 2533274790395905, "start_id": 1970324836974593, "properties": {}}::edge, {"id": 2533274790395905, "label": "end", "properties": {"i": [3, "test", [1, 2, 3], {"id": 1}, 1.0, 1::numeric], "j": 3}}::vertex]::path
+(1 row)
+
+-- test empty lists
+SELECT * FROM cypher('cypher_set', $$MATCH (n) SET n.i = [] RETURN n$$) AS (a agtype);
+ a
+--------------------------------------------------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"i": [], "j": 5, "y": 2}}::vertex
+ {"id": 281474976710658, "label": "", "properties": {"i": [], "j": 5, "t": 150, "y": 1}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"i": [], "j": 5, "t": 150}}::vertex
+ {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": [], "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
+ {"id": 844424930131969, "label": "v", "properties": {"i": [], "j": 5, "t": 150}}::vertex
+ {"id": 844424930131971, "label": "v", "properties": {"i": [], "j": 5, "t": 150}}::vertex
+ {"id": 1407374883553281, "label": "other_v", "properties": {"i": [], "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553282, "label": "other_v", "properties": {"i": [], "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553283, "label": "other_v", "properties": {"i": [], "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553284, "label": "other_v", "properties": {"i": [], "j": 5, "k": 10}}::vertex
+ {"id": 1688849860263937, "label": "vertices", "properties": {"i": [], "j": 6, "k": 9}}::vertex
+ {"id": 1970324836974593, "label": "begin", "properties": {"i": [], "j": 4}}::vertex
+ {"id": 2533274790395905, "label": "end", "properties": {"i": [], "j": 3}}::vertex
+(13 rows)
+
+SELECT * FROM cypher('cypher_set', $$MATCH (n) RETURN n$$) AS (a agtype);
+ a
+--------------------------------------------------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"i": [], "j": 5, "y": 2}}::vertex
+ {"id": 281474976710658, "label": "", "properties": {"i": [], "j": 5, "t": 150, "y": 1}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"i": [], "j": 5, "t": 150}}::vertex
+ {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": [], "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
+ {"id": 844424930131969, "label": "v", "properties": {"i": [], "j": 5, "t": 150}}::vertex
+ {"id": 844424930131971, "label": "v", "properties": {"i": [], "j": 5, "t": 150}}::vertex
+ {"id": 1407374883553281, "label": "other_v", "properties": {"i": [], "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553282, "label": "other_v", "properties": {"i": [], "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553283, "label": "other_v", "properties": {"i": [], "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553284, "label": "other_v", "properties": {"i": [], "j": 5, "k": 10}}::vertex
+ {"id": 1688849860263937, "label": "vertices", "properties": {"i": [], "j": 6, "k": 9}}::vertex
+ {"id": 1970324836974593, "label": "begin", "properties": {"i": [], "j": 4}}::vertex
+ {"id": 2533274790395905, "label": "end", "properties": {"i": [], "j": 3}}::vertex
+(13 rows)
+
+-- test maps
+SELECT * FROM cypher('cypher_set', $$MATCH (n) SET n.i = {prop1: 3, prop2:'test', prop3: [1, 2, 3], prop4: {id: 1}, prop5: 1.0, prop6:1.0::numeric} RETURN n$$) AS (a agtype);
+ a
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "y": 2}}::vertex
+ {"id": 281474976710658, "label": "", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "t": 150, "y": 1}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "t": 150}}::vertex
+ {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
+ {"id": 844424930131969, "label": "v", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "t": 150}}::vertex
+ {"id": 844424930131971, "label": "v", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "t": 150}}::vertex
+ {"id": 1407374883553281, "label": "other_v", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553282, "label": "other_v", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553283, "label": "other_v", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553284, "label": "other_v", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "k": 10}}::vertex
+ {"id": 1688849860263937, "label": "vertices", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 6, "k": 9}}::vertex
+ {"id": 1970324836974593, "label": "begin", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 4}}::vertex
+ {"id": 2533274790395905, "label": "end", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 3}}::vertex
+(13 rows)
+
+SELECT * FROM cypher('cypher_set', $$MATCH (n) RETURN n$$) AS (a agtype);
+ a
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "y": 2}}::vertex
+ {"id": 281474976710658, "label": "", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "t": 150, "y": 1}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "t": 150}}::vertex
+ {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
+ {"id": 844424930131969, "label": "v", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "t": 150}}::vertex
+ {"id": 844424930131971, "label": "v", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "t": 150}}::vertex
+ {"id": 1407374883553281, "label": "other_v", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553282, "label": "other_v", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553283, "label": "other_v", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553284, "label": "other_v", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 5, "k": 10}}::vertex
+ {"id": 1688849860263937, "label": "vertices", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 6, "k": 9}}::vertex
+ {"id": 1970324836974593, "label": "begin", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 4}}::vertex
+ {"id": 2533274790395905, "label": "end", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": 1}, "prop5": 1.0, "prop6": 1::numeric}, "j": 3}}::vertex
+(13 rows)
+
+-- test maps in paths
+SELECT * FROM cypher('cypher_set', $$MATCH p=(u:begin)-[:edge]->(v:end) SET u.i = {prop1: 1, prop2: 2, prop3: 3} return u, p $$) AS (u agtype, p agtype);
+ u | p [...]
+-----------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ [...]
+ {"id": 1970324836974593, "label": "begin", "properties": {"i": {"prop1": 1, "prop2": 2, "prop3": 3}, "j": 4}}::vertex | [{"id": 1970324836974593, "label": "begin", "properties": {"i": {"prop1": 1, "prop2": 2, "prop3": 3}, "j": 4}}::vertex, {"id": 2251799813685249, "label": "edge", "end_id": 2533274790395905, "start_id": 1970324836974593, "properties": {}}::edge, {"id": 2533274790395905, "label": "end", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": [...]
+(1 row)
+
+SELECT * FROM cypher('cypher_set', $$MATCH p=(u:begin)-[:edge]->(v:end) return u, p $$) AS (u agtype, p agtype);
+ u | p [...]
+-----------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ [...]
+ {"id": 1970324836974593, "label": "begin", "properties": {"i": {"prop1": 1, "prop2": 2, "prop3": 3}, "j": 4}}::vertex | [{"id": 1970324836974593, "label": "begin", "properties": {"i": {"prop1": 1, "prop2": 2, "prop3": 3}, "j": 4}}::vertex, {"id": 2251799813685249, "label": "edge", "end_id": 2533274790395905, "start_id": 1970324836974593, "properties": {}}::edge, {"id": 2533274790395905, "label": "end", "properties": {"i": {"prop1": 3, "prop2": "test", "prop3": [1, 2, 3], "prop4": {"id": [...]
+(1 row)
+
+-- test empty maps
+SELECT * FROM cypher('cypher_set', $$MATCH (n) SET n.i = {} RETURN n$$) AS (a agtype);
+ a
+--------------------------------------------------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"i": {}, "j": 5, "y": 2}}::vertex
+ {"id": 281474976710658, "label": "", "properties": {"i": {}, "j": 5, "t": 150, "y": 1}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"i": {}, "j": 5, "t": 150}}::vertex
+ {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": {}, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
+ {"id": 844424930131969, "label": "v", "properties": {"i": {}, "j": 5, "t": 150}}::vertex
+ {"id": 844424930131971, "label": "v", "properties": {"i": {}, "j": 5, "t": 150}}::vertex
+ {"id": 1407374883553281, "label": "other_v", "properties": {"i": {}, "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553282, "label": "other_v", "properties": {"i": {}, "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553283, "label": "other_v", "properties": {"i": {}, "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553284, "label": "other_v", "properties": {"i": {}, "j": 5, "k": 10}}::vertex
+ {"id": 1688849860263937, "label": "vertices", "properties": {"i": {}, "j": 6, "k": 9}}::vertex
+ {"id": 1970324836974593, "label": "begin", "properties": {"i": {}, "j": 4}}::vertex
+ {"id": 2533274790395905, "label": "end", "properties": {"i": {}, "j": 3}}::vertex
+(13 rows)
+
+SELECT * FROM cypher('cypher_set', $$MATCH (n) RETURN n$$) AS (a agtype);
+ a
+--------------------------------------------------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"i": {}, "j": 5, "y": 2}}::vertex
+ {"id": 281474976710658, "label": "", "properties": {"i": {}, "j": 5, "t": 150, "y": 1}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"i": {}, "j": 5, "t": 150}}::vertex
+ {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": {}, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
+ {"id": 844424930131969, "label": "v", "properties": {"i": {}, "j": 5, "t": 150}}::vertex
+ {"id": 844424930131971, "label": "v", "properties": {"i": {}, "j": 5, "t": 150}}::vertex
+ {"id": 1407374883553281, "label": "other_v", "properties": {"i": {}, "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553282, "label": "other_v", "properties": {"i": {}, "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553283, "label": "other_v", "properties": {"i": {}, "j": 5, "k": 10}}::vertex
+ {"id": 1407374883553284, "label": "other_v", "properties": {"i": {}, "j": 5, "k": 10}}::vertex
+ {"id": 1688849860263937, "label": "vertices", "properties": {"i": {}, "j": 6, "k": 9}}::vertex
+ {"id": 1970324836974593, "label": "begin", "properties": {"i": {}, "j": 4}}::vertex
+ {"id": 2533274790395905, "label": "end", "properties": {"i": {}, "j": 3}}::vertex
+(13 rows)
+
--
-- Clean up
--
@@ -516,5 +690,3 @@ NOTICE: graph "cypher_set" has been dropped
(1 row)
--
--- End
---
diff --git a/regress/sql/cypher_set.sql b/regress/sql/cypher_set.sql
index f3b762f..76a3a02 100644
--- a/regress/sql/cypher_set.sql
+++ b/regress/sql/cypher_set.sql
@@ -174,6 +174,36 @@ END;
SELECT * FROM cypher('cypher_set', $$MATCH (u:vertices) return u $$) AS (result agtype);
SELECT * FROM cypher('cypher_set', $$MATCH (u:begin)-[:edge]->(v:end) return u, v $$) AS (u agtype, v agtype);
+-- test lists
+SELECT * FROM cypher('cypher_set', $$MATCH (n) SET n.i = [3, 'test', [1, 2, 3], {id: 1}, 1.0, 1.0::numeric] RETURN n$$) AS (a agtype);
+
+SELECT * FROM cypher('cypher_set', $$MATCH (n) RETURN n$$) AS (a agtype);
+
+-- test that lists get updated in paths
+SELECT * FROM cypher('cypher_set', $$MATCH p=(u:begin)-[:edge]->(v:end) SET u.i = [1, 2, 3] return u, p $$) AS (u agtype, p agtype);
+
+SELECT * FROM cypher('cypher_set', $$MATCH p=(u:begin)-[:edge]->(v:end) return u, p $$) AS (u agtype, p agtype);
+
+-- test empty lists
+SELECT * FROM cypher('cypher_set', $$MATCH (n) SET n.i = [] RETURN n$$) AS (a agtype);
+
+SELECT * FROM cypher('cypher_set', $$MATCH (n) RETURN n$$) AS (a agtype);
+
+-- test maps
+SELECT * FROM cypher('cypher_set', $$MATCH (n) SET n.i = {prop1: 3, prop2:'test', prop3: [1, 2, 3], prop4: {id: 1}, prop5: 1.0, prop6:1.0::numeric} RETURN n$$) AS (a agtype);
+
+SELECT * FROM cypher('cypher_set', $$MATCH (n) RETURN n$$) AS (a agtype);
+
+-- test maps in paths
+SELECT * FROM cypher('cypher_set', $$MATCH p=(u:begin)-[:edge]->(v:end) SET u.i = {prop1: 1, prop2: 2, prop3: 3} return u, p $$) AS (u agtype, p agtype);
+
+SELECT * FROM cypher('cypher_set', $$MATCH p=(u:begin)-[:edge]->(v:end) return u, p $$) AS (u agtype, p agtype);
+
+-- test empty maps
+SELECT * FROM cypher('cypher_set', $$MATCH (n) SET n.i = {} RETURN n$$) AS (a agtype);
+
+SELECT * FROM cypher('cypher_set', $$MATCH (n) RETURN n$$) AS (a agtype);
+
--
-- Clean up
--
@@ -182,5 +212,4 @@ DROP FUNCTION set_test;
SELECT drop_graph('cypher_set', true);
--
--- End
---
+
diff --git a/src/backend/executor/cypher_set.c b/src/backend/executor/cypher_set.c
index c9fba79..9b37b3b 100644
--- a/src/backend/executor/cypher_set.c
+++ b/src/backend/executor/cypher_set.c
@@ -99,7 +99,9 @@ static void begin_cypher_set(CustomScanState *node, EState *estate,
* that have modified the command id.
*/
if (estate->es_output_cid == 0)
+ {
estate->es_output_cid = estate->es_snapshot->curcid;
+ }
Increment_Estate_CommandId(estate);
}
@@ -133,7 +135,9 @@ static HeapTuple update_entity_tuple(ResultRelInfo *resultRelInfo,
// Check the constraints of the tuple
tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
if (resultRelInfo->ri_RelationDesc->rd_att->constr != NULL)
+ {
ExecConstraints(resultRelInfo, elemTupleSlot, estate);
+ }
// Insert the tuple normally
update_result = heap_update(resultRelInfo->ri_RelationDesc,
@@ -143,14 +147,18 @@ static HeapTuple update_entity_tuple(ResultRelInfo *resultRelInfo,
&lockmode);
if (update_result != HeapTupleMayBeUpdated)
- ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),
- errmsg("Entity failed to be updated: %i",
- update_result)));
+ {
+ ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("Entity failed to be updated: %i",
+ update_result)));
+ }
// Insert index entries for the tuple
if (resultRelInfo->ri_NumIndices > 0)
+ {
ExecInsertIndexTuples(elemTupleSlot, &(tuple->t_self), estate,
false, NULL, NIL);
+ }
}
ReleaseBuffer(buffer);
@@ -179,6 +187,11 @@ static void process_all_tuples(CustomScanState *node)
} while (!TupIsNull(slot));
}
+/*
+ * Checks the path to see if the entities contained within
+ * have the same graphid and the updated_id field. Returns
+ * true if yes, false otherwise.
+ */
static bool check_path(agtype_value *path, graphid updated_id)
{
int i;
@@ -190,12 +203,18 @@ static bool check_path(agtype_value *path, graphid updated_id)
agtype_value *id = GET_AGTYPE_VALUE_OBJECT_VALUE(elem, "id");
if (updated_id == id->val.int_value)
+ {
return true;
+ }
}
return false;
}
+/*
+ * Construct a new agtype path with the entity with updated_id
+ * replacing all of its intances in path with updated_entity
+ */
static agtype_value *replace_entity_in_path(agtype_value *path,
graphid updated_id,
agtype *updated_entity)
@@ -217,24 +236,37 @@ static agtype_value *replace_entity_in_path(agtype_value *path,
parsed_agtype_value = push_agtype_value(&parse_state, tok,
tok < WAGT_BEGIN_ARRAY ? r : NULL);
+ // Iterate through the path, replace entities as necessary.
for (i = 0; i < path->val.array.num_elems; i++)
{
agtype_value *id, *elem;
elem = &path->val.array.elems[i];
+ // something unexpected happended, throw an error.
if (elem->type != AGTV_VERTEX && elem->type != AGTV_EDGE)
+ {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unsupported agtype found in a path")));
+ }
+ // extract the id field
id = GET_AGTYPE_VALUE_OBJECT_VALUE(elem, "id");
+ /*
+ * Either replace or keep the entity in the new path, depending on the id
+ * check.
+ */
if (updated_id == id->val.int_value)
+ {
parsed_agtype_value = push_agtype_value(&parse_state, WAGT_ELEM,
get_ith_agtype_value_from_container(&updated_entity->root, 0));
+ }
else
+ {
parsed_agtype_value = push_agtype_value(&parse_state, WAGT_ELEM,
elem);
+ }
}
parsed_agtype_value = push_agtype_value(&parse_state, WAGT_END_ARRAY, NULL);
@@ -243,6 +275,11 @@ static agtype_value *replace_entity_in_path(agtype_value *path,
return parsed_agtype_value;
}
+/*
+ * When a vertex or edge is updated, we need to update the vertex
+ * or edge if it is contained within a path. Scan through scanTupleSlot
+ * to find all paths and check if they need to be updated.
+ */
static void update_all_paths(CustomScanState *node, graphid id,
agtype *updated_entity)
{
@@ -256,19 +293,35 @@ static void update_all_paths(CustomScanState *node, graphid id,
agtype *original_entity;
agtype_value *original_entity_value;
+ // skip non agtype values
if (scanTupleSlot->tts_tupleDescriptor->attrs[i].atttypid != AGTYPEOID)
+ {
continue;
+ }
+ // skip nulls
if (scanTupleSlot->tts_isnull[i])
+ {
continue;
+ }
original_entity = DATUM_GET_AGTYPE_P(scanTupleSlot->tts_values[i]);
+
+ // if the value is not a scalar type, its not a path
+ if (!AGTYPE_CONTAINER_IS_SCALAR(&original_entity->root))
+ {
+ continue;
+ }
+
original_entity_value = get_ith_agtype_value_from_container(&original_entity->root, 0);
+ // we found a path
if (original_entity_value->type == AGTV_PATH)
{
+ // check if the path contains the entity.
if (check_path(original_entity_value, id))
{
+ // the path does contain the entity replace with the new entity.
agtype_value *new_path = replace_entity_in_path(original_entity_value, id, updated_entity);
scanTupleSlot->tts_values[i] = AGTYPE_P_GET_DATUM(agtype_value_to_agtype(new_path));
@@ -340,23 +393,29 @@ static void process_update_list(CustomScanState *node)
* possible when the OPTIONAL MATCH clause is implemented.
*/
if (scanTupleSlot->tts_isnull[update_item->entity_position - 1])
+ {
continue;
+ }
if (scanTupleSlot->tts_tupleDescriptor->attrs[update_item->entity_position -1].atttypid != AGTYPEOID)
+ {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("age %s clause can only update agtype",
clause_name)));
+ }
original_entity = DATUM_GET_AGTYPE_P(scanTupleSlot->tts_values[update_item->entity_position - 1]);
original_entity_value = get_ith_agtype_value_from_container(&original_entity->root, 0);
if (original_entity_value->type != AGTV_VERTEX &&
original_entity_value->type != AGTV_EDGE)
+ {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("age %s clause can only update vertex and edges",
clause_name)));
+ }
/* get the id and label for later */
id = GET_AGTYPE_VALUE_OBJECT_VALUE(original_entity_value, "id");
@@ -374,18 +433,26 @@ static void process_update_list(CustomScanState *node)
* MATCH is implemented.
*/
if(update_item->remove_item)
+ {
remove_property = true;
+ }
else
+ {
remove_property = scanTupleSlot->tts_isnull[update_item->prop_position - 1];
+ }
/*
* If we need to remove the property, set the value to NULL. Otherwise
* fetch the evaluated expression from the tuble slot.
*/
if (remove_property)
+ {
new_property_value = NULL;
+ }
else
+ {
new_property_value = DATUM_GET_AGTYPE_P(scanTupleSlot->tts_values[update_item->prop_position - 1]);
+ }
/*
* Alter the properties Agtype value to contain or remove the updated
@@ -508,7 +575,9 @@ static TupleTableSlot *exec_cypher_set(CustomScanState *node)
Increment_Estate_CommandId(estate);
if (TupIsNull(slot))
+ {
return NULL;
+ }
econtext->ecxt_scantuple =
node->ss.ps.lefttree->ps_ProjInfo->pi_exprContext->ecxt_scantuple;
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index 6199c67..240ad72 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -165,6 +165,7 @@ static int extract_variadic_args_min(FunctionCallInfo fcinfo,
int variadic_start, bool convert_unknown,
Datum **args, Oid **types, bool **nulls,
int min_num_args);
+agtype_value *agtype_composite_to_agtype_value_binary(agtype *a);
/* fast helper function to test for AGTV_NULL in an agtype */
bool is_agtype_null(agtype *agt_arg)
@@ -7937,7 +7938,37 @@ Datum age_timestamp(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
}
-agtype_value *alter_property_value(agtype_value *properties, char *var_name, agtype *new_v, bool remove_property)
+/*
+ * Converts an agtype object or array to a binary agtype_value.
+ */
+agtype_value *agtype_composite_to_agtype_value_binary(agtype *a)
+{
+ agtype_value *result;
+
+ if (AGTYPE_CONTAINER_IS_SCALAR(&a->root))
+ {
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot convert agtype scalar objects to binary agtype_value objects")));
+ }
+
+ result = palloc(sizeof(agtype_value));
+
+ // convert the agtype to a binary agtype_value
+ result->type = AGTV_BINARY;
+ result->val.binary.len = AGTYPE_CONTAINER_SIZE(&a->root);
+ result->val.binary.data = &a->root;
+
+ return result;
+}
+
+/*
+ * For the given properties, update the property with the key equal
+ * to var_name with the value defined in new_v. If the remove_property
+ * flag is set, simply remove the property with the given property
+ * name instead.
+ */
+agtype_value *alter_property_value(agtype_value *properties, char *var_name,
+ agtype *new_v, bool remove_property)
{
agtype_iterator *it;
agtype_iterator_token tok = WAGT_DONE;
@@ -7947,12 +7978,18 @@ agtype_value *alter_property_value(agtype_value *properties, char *var_name, agt
agtype_value *parsed_agtype_value = NULL;
bool found;
+ // if no properties, return NULL
if (properties == NULL)
+ {
return NULL;
+ }
+ // if properties is not an object, throw an error
if (properties->type != AGTV_OBJECT)
+ {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("can only update objects")));
+ }
r = palloc0(sizeof(agtype_value));
@@ -7962,8 +7999,14 @@ agtype_value *alter_property_value(agtype_value *properties, char *var_name, agt
parsed_agtype_value = push_agtype_value(&parse_state, tok, tok < WAGT_BEGIN_ARRAY ? r : NULL);
+ /*
+ * If the new value is NULL, this is equivalent to the remove_property
+ * flag set to true.
+ */
if (new_v == NULL)
+ {
remove_property = true;
+ }
found = false;
while (true)
@@ -7973,36 +8016,68 @@ agtype_value *alter_property_value(agtype_value *properties, char *var_name, agt
tok = agtype_iterator_next(&it, r, true);
if (tok == WAGT_DONE || tok == WAGT_END_OBJECT)
+ {
break;
+ }
str = pnstrdup(r->val.string.val, r->val.string.len);
+
+ /*
+ * Check the key value, if it is equal to the passed in
+ * var_name, replace the value for this key with the passed
+ * in agtype. Otherwise pass the existing value to the
+ * new properties agtype_value.
+ */
if (strcmp(str, var_name))
{
+ // push the key
parsed_agtype_value = push_agtype_value(
&parse_state, tok, tok < WAGT_BEGIN_ARRAY ? r : NULL);
+ // get the value and push the value
tok = agtype_iterator_next(&it, r, true);
-
parsed_agtype_value = push_agtype_value(&parse_state, tok, r);
}
else
{
agtype_value *new_agtype_value_v;
- if (remove_property)
+ // if the remove flag is set, don't push the key or any value
+ if(remove_property)
{
+ // skip the value
tok = agtype_iterator_next(&it, r, true);
continue;
}
+ // push the key
parsed_agtype_value = push_agtype_value(
&parse_state, tok, tok < WAGT_BEGIN_ARRAY ? r : NULL);
- new_agtype_value_v = get_ith_agtype_value_from_container(&new_v->root, 0);
-
+ // skip the existing value for the key
tok = agtype_iterator_next(&it, r, true);
- parsed_agtype_value = push_agtype_value(&parse_state, tok, new_agtype_value_v);
+ /*
+ * If the the new agtype is scalar, push the agtype_value to the
+ * parse state. If the agtype is an object or array convert the
+ * agtype to a binary agtype_value to pass to the parse_state.
+ * This will save uncessary deserialization and serialization
+ * logic from running.
+ */
+ if (AGTYPE_CONTAINER_IS_SCALAR(&new_v->root))
+ {
+ //get the scalar value and push as the value
+ new_agtype_value_v = get_ith_agtype_value_from_container(&new_v->root, 0);
+
+ parsed_agtype_value = push_agtype_value(&parse_state, WAGT_VALUE, new_agtype_value_v);
+ }
+ else
+ {
+ agtype_value *result = agtype_composite_to_agtype_value_binary(new_v);
+
+ parsed_agtype_value = push_agtype_value(&parse_state, WAGT_VALUE, result);
+ }
+
found = true;
}
}
@@ -8014,17 +8089,35 @@ agtype_value *alter_property_value(agtype_value *properties, char *var_name, agt
if (!found && !remove_property)
{
agtype_value *new_agtype_value_v;
+ agtype_value *key = string_to_agtype_value(var_name);
+ // push the new key
parsed_agtype_value = push_agtype_value(
- &parse_state, WAGT_KEY, string_to_agtype_value(var_name));
+ &parse_state, WAGT_KEY, key);
- new_agtype_value_v = get_ith_agtype_value_from_container(&new_v->root, 0);
+ /*
+ * If the the new agtype is scalar, push the agtype_value to the
+ * parse state. If the agtype is an object or array convert the
+ * agtype to a binary agtype_value to pass to the parse_state.
+ * This will save uncessary deserialization and serialization
+ * logic from running.
+ */
+ if (AGTYPE_CONTAINER_IS_SCALAR(&new_v->root))
+ {
+ new_agtype_value_v = get_ith_agtype_value_from_container(&new_v->root, 0);
- tok = agtype_iterator_next(&it, r, true);
+ // convert the agtype array or object to a binary agtype_value
+ parsed_agtype_value = push_agtype_value(&parse_state, WAGT_VALUE, new_agtype_value_v);
+ }
+ else
+ {
+ agtype_value *result = agtype_composite_to_agtype_value_binary(new_v);
- parsed_agtype_value = push_agtype_value(&parse_state, WAGT_VALUE, new_agtype_value_v);
+ parsed_agtype_value = push_agtype_value(&parse_state, WAGT_VALUE, result);
+ }
}
+ // push the end object token to parse state
parsed_agtype_value = push_agtype_value(&parse_state, WAGT_END_OBJECT, NULL);
return parsed_agtype_value;