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 2020/10/03 00:53:50 UTC
[incubator-age] branch master updated: Support for Agtype BTree and
Hash Indices and Distinct 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 91d4525 Support for Agtype BTree and Hash Indices and Distinct Clause
91d4525 is described below
commit 91d4525edaa42492b2542c200a949f25b4883b71
Author: Josh Innis <jo...@gmail.com>
AuthorDate: Tue Sep 29 13:35:24 2020 -0700
Support for Agtype BTree and Hash Indices and Distinct Clause
Also fixed a type casting issue with the MATCH clause
---
age--0.2.0.sql | 32 ++++
regress/expected/agtype.out | 308 +++++++++++++++++++++++++++++++++++-
regress/expected/cypher_match.out | 53 ++++++-
regress/sql/agtype.sql | 92 ++++++++++-
regress/sql/cypher_match.sql | 28 ++++
src/backend/parser/cypher_clause.c | 11 +-
src/backend/utils/adt/agtype.c | 70 +++++++-
src/backend/utils/adt/agtype_util.c | 59 +++++--
src/include/utils/agtype.h | 5 +
9 files changed, 639 insertions(+), 19 deletions(-)
diff --git a/age--0.2.0.sql b/age--0.2.0.sql
index a91615d..92c9ae3 100644
--- a/age--0.2.0.sql
+++ b/age--0.2.0.sql
@@ -557,6 +557,38 @@ CREATE OPERATOR >= (
JOIN = scalargejoinsel
);
+CREATE FUNCTION agtype_btree_cmp(agtype, agtype)
+RETURNS INTEGER
+LANGUAGE c
+STABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE OPERATOR CLASS agtype_ops_btree
+ DEFAULT
+ FOR TYPE agtype
+ USING btree AS
+ OPERATOR 1 <,
+ OPERATOR 2 <=,
+ OPERATOR 3 =,
+ OPERATOR 4 >,
+ OPERATOR 5 >=,
+ FUNCTION 1 agtype_btree_cmp(agtype, agtype);
+
+CREATE FUNCTION agtype_hash_cmp(agtype)
+RETURNS INTEGER
+LANGUAGE c
+STABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE OPERATOR CLASS agtype_ops_hash
+ DEFAULT
+ FOR TYPE agtype
+ USING hash AS
+ OPERATOR 1 =,
+ FUNCTION 1 agtype_hash_cmp(agtype);
+
--
-- graph id conversion function
--
diff --git a/regress/expected/agtype.out b/regress/expected/agtype.out
index 40879d6..ddb3bc9 100644
--- a/regress/expected/agtype.out
+++ b/regress/expected/agtype.out
@@ -1231,7 +1231,7 @@ SELECT agtype_access_operator(
--Edge in a list
SELECT agtype_build_list(
- _agtype_build_edge('1'::graphid, '2'::graphid, '3'::graphid,
+ _agtype_build_edge('1'::graphid, '2'::graphid, '3'::graphid,
$$label_name$$, agtype_build_map()),
_agtype_build_edge('2'::graphid, '2'::graphid, '3'::graphid,
$$label_name$$, agtype_build_map()));
@@ -1432,6 +1432,312 @@ SELECT agtype_string_match_contains('"abcdefghijklmnopqrstuvwxyz"', '"hijl"');
false
(1 row)
+--Agtype Hash Comparison Function
+SELECT agtype_hash_cmp(NULL);
+ agtype_hash_cmp
+-----------------
+ 0
+(1 row)
+
+SELECT agtype_hash_cmp('1'::agtype);
+ agtype_hash_cmp
+-----------------
+ -123017199
+(1 row)
+
+SELECT agtype_hash_cmp('1.0'::agtype);
+ agtype_hash_cmp
+-----------------
+ 614780178
+(1 row)
+
+SELECT agtype_hash_cmp('"1"'::agtype);
+ agtype_hash_cmp
+-----------------
+ -888576106
+(1 row)
+
+SELECT agtype_hash_cmp('[1]'::agtype);
+ agtype_hash_cmp
+-----------------
+ 434414509
+(1 row)
+
+SELECT agtype_hash_cmp('[1, 1]'::agtype);
+ agtype_hash_cmp
+-----------------
+ -1551022880
+(1 row)
+
+SELECT agtype_hash_cmp('[1, 1, 1]'::agtype);
+ agtype_hash_cmp
+-----------------
+ -3900769
+(1 row)
+
+SELECT agtype_hash_cmp('[1, 1, 1, 1]'::agtype);
+ agtype_hash_cmp
+-----------------
+ 1756986519
+(1 row)
+
+SELECT agtype_hash_cmp('[1, 1, 1, 1, 1]'::agtype);
+ agtype_hash_cmp
+-----------------
+ -47741579
+(1 row)
+
+SELECT agtype_hash_cmp('[[1]]'::agtype);
+ agtype_hash_cmp
+-----------------
+ 878744030
+(1 row)
+
+SELECT agtype_hash_cmp('[[1, 1]]'::agtype);
+ agtype_hash_cmp
+-----------------
+ -1254522284
+(1 row)
+
+SELECT agtype_hash_cmp('[[1], 1]'::agtype);
+ agtype_hash_cmp
+-----------------
+ -1005036
+(1 row)
+
+SELECT agtype_hash_cmp('[1543872]'::agtype);
+ agtype_hash_cmp
+-----------------
+ -1925093371
+(1 row)
+
+SELECT agtype_hash_cmp('[1, "abcde", 2.0]'::agtype);
+ agtype_hash_cmp
+-----------------
+ -1128310748
+(1 row)
+
+SELECT agtype_hash_cmp(agtype_in('null'));
+ agtype_hash_cmp
+-----------------
+ -505290271
+(1 row)
+
+SELECT agtype_hash_cmp(agtype_in('[null]'));
+ agtype_hash_cmp
+-----------------
+ 505290241
+(1 row)
+
+SELECT agtype_hash_cmp(agtype_in('[null, null]'));
+ agtype_hash_cmp
+-----------------
+ 3
+(1 row)
+
+SELECT agtype_hash_cmp(agtype_in('[null, null, null]'));
+ agtype_hash_cmp
+-----------------
+ 2021160967
+(1 row)
+
+SELECT agtype_hash_cmp(agtype_in('[null, null, null, null]'));
+ agtype_hash_cmp
+-----------------
+ 15
+(1 row)
+
+SELECT agtype_hash_cmp(agtype_in('[null, null, null, null, null]'));
+ agtype_hash_cmp
+-----------------
+ -505290721
+(1 row)
+
+SELECT agtype_hash_cmp('{"id":1, "label":"test", "properties":{"id":100}}'::agtype);
+ agtype_hash_cmp
+-----------------
+ 1116453668
+(1 row)
+
+SELECT agtype_hash_cmp('{"id":1, "label":"test", "properties":{"id":100}}::vertex'::agtype);
+ agtype_hash_cmp
+-----------------
+ 1848106598
+(1 row)
+
+SELECT agtype_hash_cmp('{"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}'::agtype);
+ agtype_hash_cmp
+-----------------
+ 1064722414
+(1 row)
+
+SELECT agtype_hash_cmp('{"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}::edge'::agtype);
+ agtype_hash_cmp
+-----------------
+ -1790838958
+(1 row)
+
+SELECT agtype_hash_cmp('
+ [{"id":1, "label":"test", "properties":{"id":100}}::vertex,
+ {"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}::edge,
+ {"id":5, "label":"vlabel", "properties":{}}::vertex]'::agtype);
+ agtype_hash_cmp
+-----------------
+ -231467898
+(1 row)
+
+SELECT agtype_hash_cmp('
+ [{"id":1, "label":"test", "properties":{"id":100}}::vertex,
+ {"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}::edge,
+ {"id":5, "label":"vlabel", "properties":{}}::vertex]::path'::agtype);
+ agtype_hash_cmp
+-----------------
+ 843330291
+(1 row)
+
+--Agtype BTree Comparison Function
+SELECT agtype_btree_cmp('1'::agtype, '1'::agtype);
+ agtype_btree_cmp
+------------------
+ 0
+(1 row)
+
+SELECT agtype_btree_cmp('1'::agtype, '1.0'::agtype);
+ agtype_btree_cmp
+------------------
+ 0
+(1 row)
+
+SELECT agtype_btree_cmp('1'::agtype, '"1"'::agtype);
+ agtype_btree_cmp
+------------------
+ 1
+(1 row)
+
+SELECT agtype_btree_cmp('"string"'::agtype, '"string"'::agtype);
+ agtype_btree_cmp
+------------------
+ 0
+(1 row)
+
+SELECT agtype_btree_cmp('"string"'::agtype, '"string "'::agtype);
+ agtype_btree_cmp
+------------------
+ -1
+(1 row)
+
+SELECT agtype_btree_cmp(NULL, NULL);
+ agtype_btree_cmp
+------------------
+ 0
+(1 row)
+
+SELECT agtype_btree_cmp(NULL, '1'::agtype);
+ agtype_btree_cmp
+------------------
+ 1
+(1 row)
+
+SELECT agtype_btree_cmp('1'::agtype, NULL);
+ agtype_btree_cmp
+------------------
+ -1
+(1 row)
+
+SELECT agtype_btree_cmp(agtype_in('null'), NULL);
+ agtype_btree_cmp
+------------------
+ -1
+(1 row)
+
+SELECT agtype_btree_cmp(
+ '1'::agtype,
+ '{"id":1, "label":"test", "properties":{"id":100}}::vertex'::agtype);
+ agtype_btree_cmp
+------------------
+ 1
+(1 row)
+
+SELECT agtype_btree_cmp(
+ '{"id":1, "label":"test", "properties":{"id":100}}'::agtype,
+ '{"id":1, "label":"test", "properties":{"id":100}}'::agtype);
+ agtype_btree_cmp
+------------------
+ 0
+(1 row)
+
+SELECT agtype_btree_cmp(
+ '{"id":1, "label":"test", "properties":{"id":100}}'::agtype,
+ '{"id":1, "label":"test", "properties":{"id":200}}'::agtype);
+ agtype_btree_cmp
+------------------
+ -1
+(1 row)
+
+SELECT agtype_btree_cmp(
+ '{"id":1, "label":"test", "properties":{"id":100}}::vertex'::agtype,
+ '{"id":1, "label":"test", "properties":{"id":100}}::vertex'::agtype);
+ agtype_btree_cmp
+------------------
+ 0
+(1 row)
+
+SELECT agtype_btree_cmp(
+ '{"id":1, "label":"test", "properties":{"id":100}}::vertex'::agtype,
+ '{"id":1, "label":"test", "properties":{"id":200}}::vertex'::agtype);
+ agtype_btree_cmp
+------------------
+ 0
+(1 row)
+
+SELECT agtype_btree_cmp(
+ '{"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}::edge'::agtype,
+ '{"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}::edge'::agtype);
+ agtype_btree_cmp
+------------------
+ 0
+(1 row)
+
+SELECT agtype_btree_cmp(
+ '{"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{"prop1": 1}}::edge'::agtype,
+ '{"id":2, "start_id":4, "end_id": 5, "label":"elabel", "properties":{"prop2": 2}}::edge'::agtype);
+ agtype_btree_cmp
+------------------
+ 0
+(1 row)
+
+SELECT agtype_btree_cmp(
+ '{"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{"prop1": 1}}::edge'::agtype,
+ '{"id":8, "start_id":4, "end_id": 5, "label":"elabel", "properties":{"prop2": 2}}::edge'::agtype);
+ agtype_btree_cmp
+------------------
+ -1
+(1 row)
+
+SELECT agtype_btree_cmp(
+ '[{"id":1, "label":"test", "properties":{"id":100}}::vertex,
+ {"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}::edge,
+ {"id":3, "label":"vlabel", "properties":{}}::vertex]::path'::agtype,
+ '[{"id":1, "label":"test", "properties":{"id":100}}::vertex,
+ {"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}::edge,
+ {"id":3, "label":"vlabel", "properties":{}}::vertex]::path'::agtype);
+ agtype_btree_cmp
+------------------
+ 0
+(1 row)
+
+SELECT agtype_btree_cmp(
+ '[{"id":1, "label":"test", "properties":{"id":100}}::vertex,
+ {"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}::edge,
+ {"id":3, "label":"vlabel", "properties":{}}::vertex]::path'::agtype,
+ '[{"id":1, "label":"test", "properties":{"id":100}}::vertex,
+ {"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}::edge,
+ {"id":4, "label":"vlabel", "properties":{}}::vertex]::path'::agtype);
+ agtype_btree_cmp
+------------------
+ -1
+(1 row)
+
--
-- Cleanup
--
diff --git a/regress/expected/cypher_match.out b/regress/expected/cypher_match.out
index e7587d1..82c0cc3 100644
--- a/regress/expected/cypher_match.out
+++ b/regress/expected/cypher_match.out
@@ -765,10 +765,58 @@ ERROR: syntax error at or near ")"
LINE 2: $$MATCH (u) WHERE EXISTS(u) RETURN u$$)
^
--
+--Distinct
+--
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (u)
+ RETURN DISTINCT u.id
+$$) AS (i agtype);
+ i
+-----------
+
+ "end"
+ "initial"
+ "middle"
+(4 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ CREATE (u:duplicate)-[:dup_edge {id:1 }]->(:other_v)
+$$) AS (a agtype);
+ a
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (u:duplicate)
+ CREATE (u)-[:dup_edge {id:2 }]->(:other_v)
+$$) AS (a agtype);
+ a
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (u:duplicate)-[]-(:other_v)
+ RETURN DISTINCT u
+$$) AS (i agtype);
+ERROR: function ag_catalog.agtygpe_to_grapghid(agtype) does not exist
+LINE 1: SELECT * FROM cypher('cypher_match', $$
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+SELECT * FROM cypher('cypher_match', $$
+ MATCH p=(:duplicate)-[]-(:other_v)
+ RETURN DISTINCT p
+$$) AS (i agtype);
+ i
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 3377699720527873, "label": "duplicate", "properties": {}}::vertex, {"id": 3659174697238529, "label": "dup_edge", "end_id": 3940649673949185, "start_id": 3377699720527873, "properties": {"id": 1}}::edge, {"id": 3940649673949185, "label": "other_v", "properties": {}}::vertex]::path
+ [{"id": 3377699720527873, "label": "duplicate", "properties": {}}::vertex, {"id": 3659174697238530, "label": "dup_edge", "end_id": 3940649673949186, "start_id": 3377699720527873, "properties": {"id": 2}}::edge, {"id": 3940649673949186, "label": "other_v", "properties": {}}::vertex]::path
+(2 rows)
+
+--
-- Clean up
--
SELECT drop_graph('cypher_match', true);
-NOTICE: drop cascades to 11 other objects
+NOTICE: drop cascades to 14 other objects
DETAIL: drop cascades to table cypher_match._ag_label_vertex
drop cascades to table cypher_match._ag_label_edge
drop cascades to table cypher_match.v
@@ -780,6 +828,9 @@ drop cascades to table cypher_match.v3
drop cascades to table cypher_match.e3
drop cascades to table cypher_match.loop
drop cascades to table cypher_match.self
+drop cascades to table cypher_match.duplicate
+drop cascades to table cypher_match.dup_edge
+drop cascades to table cypher_match.other_v
NOTICE: graph "cypher_match" has been dropped
drop_graph
------------
diff --git a/regress/sql/agtype.sql b/regress/sql/agtype.sql
index 41ed7b9..821a68d 100644
--- a/regress/sql/agtype.sql
+++ b/regress/sql/agtype.sql
@@ -412,7 +412,7 @@ SELECT agtype_access_operator(
--Edge in a list
SELECT agtype_build_list(
- _agtype_build_edge('1'::graphid, '2'::graphid, '3'::graphid,
+ _agtype_build_edge('1'::graphid, '2'::graphid, '3'::graphid,
$$label_name$$, agtype_build_map()),
_agtype_build_edge('2'::graphid, '2'::graphid, '3'::graphid,
$$label_name$$, agtype_build_map()));
@@ -515,6 +515,96 @@ SELECT agtype_string_match_starts_with('"abcdefghijklmnopqrstuvwxyz"', '"bcde"')
SELECT agtype_string_match_ends_with('"abcdefghijklmnopqrstuvwxyz"', '"vwxy"');
SELECT agtype_string_match_contains('"abcdefghijklmnopqrstuvwxyz"', '"hijl"');
+--Agtype Hash Comparison Function
+SELECT agtype_hash_cmp(NULL);
+SELECT agtype_hash_cmp('1'::agtype);
+SELECT agtype_hash_cmp('1.0'::agtype);
+SELECT agtype_hash_cmp('"1"'::agtype);
+SELECT agtype_hash_cmp('[1]'::agtype);
+SELECT agtype_hash_cmp('[1, 1]'::agtype);
+SELECT agtype_hash_cmp('[1, 1, 1]'::agtype);
+SELECT agtype_hash_cmp('[1, 1, 1, 1]'::agtype);
+SELECT agtype_hash_cmp('[1, 1, 1, 1, 1]'::agtype);
+SELECT agtype_hash_cmp('[[1]]'::agtype);
+SELECT agtype_hash_cmp('[[1, 1]]'::agtype);
+SELECT agtype_hash_cmp('[[1], 1]'::agtype);
+SELECT agtype_hash_cmp('[1543872]'::agtype);
+SELECT agtype_hash_cmp('[1, "abcde", 2.0]'::agtype);
+SELECT agtype_hash_cmp(agtype_in('null'));
+SELECT agtype_hash_cmp(agtype_in('[null]'));
+SELECT agtype_hash_cmp(agtype_in('[null, null]'));
+SELECT agtype_hash_cmp(agtype_in('[null, null, null]'));
+SELECT agtype_hash_cmp(agtype_in('[null, null, null, null]'));
+SELECT agtype_hash_cmp(agtype_in('[null, null, null, null, null]'));
+SELECT agtype_hash_cmp('{"id":1, "label":"test", "properties":{"id":100}}'::agtype);
+SELECT agtype_hash_cmp('{"id":1, "label":"test", "properties":{"id":100}}::vertex'::agtype);
+
+SELECT agtype_hash_cmp('{"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}'::agtype);
+SELECT agtype_hash_cmp('{"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}::edge'::agtype);
+
+SELECT agtype_hash_cmp('
+ [{"id":1, "label":"test", "properties":{"id":100}}::vertex,
+ {"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}::edge,
+ {"id":5, "label":"vlabel", "properties":{}}::vertex]'::agtype);
+
+SELECT agtype_hash_cmp('
+ [{"id":1, "label":"test", "properties":{"id":100}}::vertex,
+ {"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}::edge,
+ {"id":5, "label":"vlabel", "properties":{}}::vertex]::path'::agtype);
+
+--Agtype BTree Comparison Function
+SELECT agtype_btree_cmp('1'::agtype, '1'::agtype);
+SELECT agtype_btree_cmp('1'::agtype, '1.0'::agtype);
+SELECT agtype_btree_cmp('1'::agtype, '"1"'::agtype);
+
+SELECT agtype_btree_cmp('"string"'::agtype, '"string"'::agtype);
+SELECT agtype_btree_cmp('"string"'::agtype, '"string "'::agtype);
+
+SELECT agtype_btree_cmp(NULL, NULL);
+SELECT agtype_btree_cmp(NULL, '1'::agtype);
+SELECT agtype_btree_cmp('1'::agtype, NULL);
+SELECT agtype_btree_cmp(agtype_in('null'), NULL);
+
+SELECT agtype_btree_cmp(
+ '1'::agtype,
+ '{"id":1, "label":"test", "properties":{"id":100}}::vertex'::agtype);
+SELECT agtype_btree_cmp(
+ '{"id":1, "label":"test", "properties":{"id":100}}'::agtype,
+ '{"id":1, "label":"test", "properties":{"id":100}}'::agtype);
+SELECT agtype_btree_cmp(
+ '{"id":1, "label":"test", "properties":{"id":100}}'::agtype,
+ '{"id":1, "label":"test", "properties":{"id":200}}'::agtype);
+SELECT agtype_btree_cmp(
+ '{"id":1, "label":"test", "properties":{"id":100}}::vertex'::agtype,
+ '{"id":1, "label":"test", "properties":{"id":100}}::vertex'::agtype);
+SELECT agtype_btree_cmp(
+ '{"id":1, "label":"test", "properties":{"id":100}}::vertex'::agtype,
+ '{"id":1, "label":"test", "properties":{"id":200}}::vertex'::agtype);
+SELECT agtype_btree_cmp(
+ '{"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}::edge'::agtype,
+ '{"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}::edge'::agtype);
+SELECT agtype_btree_cmp(
+ '{"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{"prop1": 1}}::edge'::agtype,
+ '{"id":2, "start_id":4, "end_id": 5, "label":"elabel", "properties":{"prop2": 2}}::edge'::agtype);
+SELECT agtype_btree_cmp(
+ '{"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{"prop1": 1}}::edge'::agtype,
+ '{"id":8, "start_id":4, "end_id": 5, "label":"elabel", "properties":{"prop2": 2}}::edge'::agtype);
+
+SELECT agtype_btree_cmp(
+ '[{"id":1, "label":"test", "properties":{"id":100}}::vertex,
+ {"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}::edge,
+ {"id":3, "label":"vlabel", "properties":{}}::vertex]::path'::agtype,
+ '[{"id":1, "label":"test", "properties":{"id":100}}::vertex,
+ {"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}::edge,
+ {"id":3, "label":"vlabel", "properties":{}}::vertex]::path'::agtype);
+
+SELECT agtype_btree_cmp(
+ '[{"id":1, "label":"test", "properties":{"id":100}}::vertex,
+ {"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}::edge,
+ {"id":3, "label":"vlabel", "properties":{}}::vertex]::path'::agtype,
+ '[{"id":1, "label":"test", "properties":{"id":100}}::vertex,
+ {"id":2, "start_id":1, "end_id": 3, "label":"elabel", "properties":{}}::edge,
+ {"id":4, "label":"vlabel", "properties":{}}::vertex]::path'::agtype);
--
-- Cleanup
--
diff --git a/regress/sql/cypher_match.sql b/regress/sql/cypher_match.sql
index 82da895..62334a9 100644
--- a/regress/sql/cypher_match.sql
+++ b/regress/sql/cypher_match.sql
@@ -401,6 +401,34 @@ SELECT * FROM cypher('cypher_match',
AS (u agtype);
--
+--Distinct
+--
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (u)
+ RETURN DISTINCT u.id
+$$) AS (i agtype);
+
+SELECT * FROM cypher('cypher_match', $$
+ CREATE (u:duplicate)-[:dup_edge {id:1 }]->(:other_v)
+$$) AS (a agtype);
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (u:duplicate)
+ CREATE (u)-[:dup_edge {id:2 }]->(:other_v)
+$$) AS (a agtype);
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (u:duplicate)-[]-(:other_v)
+ RETURN DISTINCT u
+$$) AS (i agtype);
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH p=(:duplicate)-[]-(:other_v)
+ RETURN DISTINCT p
+$$) AS (i agtype);
+
+
+--
-- Clean up
--
diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c
index f73a5e4..dc77bbc 100644
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@ -35,6 +35,7 @@
#include "parser/parse_target.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
+#include "utils/typcache.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
@@ -965,8 +966,8 @@ static A_Expr *filter_vertices_on_label_id(cypher_parsestate *cpstate,
label_cache_data *lcd = search_label_name_graph_cache(label,
cpstate->graph_oid);
A_Const *n;
- FuncCall *fc;
- Value *ag_catalog, *extract_label_id;
+ FuncCall *fc, *conversion_fc;
+ Value *ag_catalog, *extract_label_id, *agtype_to_graphid;
int32 label_id = lcd->id;
n = makeNode(A_Const);
@@ -976,9 +977,13 @@ static A_Expr *filter_vertices_on_label_id(cypher_parsestate *cpstate,
ag_catalog = makeString("ag_catalog");
extract_label_id = makeString("_extract_label_id");
+ agtype_to_graphid = makeString("agtygpe_to_grapghid");
+
+ conversion_fc = makeFuncCall(list_make2(ag_catalog, agtype_to_graphid),
+ list_make1(id_field), -1);
fc = makeFuncCall(list_make2(ag_catalog, extract_label_id),
- list_make1(id_field), -1);
+ list_make1(conversion_fc), -1);
return makeSimpleA_Expr(AEXPR_OP, "=", (Node *)fc, (Node *)n, -1);
}
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index 429b977..e04ae78 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -121,8 +121,6 @@ static bool is_agtype_null(agtype *agt);
static agtype_value *string_to_agtype_value(char *s);
static uint64 get_edge_uniqueness_value(Datum d, Oid type, bool is_null,
int index);
-static agtype_value *get_agtype_value_object_value(agtype_value *agtv_object,
- char *key);
/* graph entity retrieval */
static Datum get_vertex(const char *graph, const char *vertex_label,
int64 graphid);
@@ -2676,6 +2674,71 @@ static bool is_agtype_null(agtype *agt)
return false;
}
+#define LEFT_ROTATE(n, i) ((n << i) | (n >> (64 - i)))
+#define RIGHT_ROTATE(n, i) ((n >> i) | (n << (64 - i)))
+
+//Hashing Function for Hash Indexes
+PG_FUNCTION_INFO_V1(agtype_hash_cmp);
+
+Datum agtype_hash_cmp(PG_FUNCTION_ARGS)
+{
+ uint64 hash = 0;
+ agtype *agt;
+ agtype_iterator *it;
+ agtype_iterator_token tok;
+ agtype_value *r;
+ uint64 seed = 0xF0F0F0F0;
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_INT16(0);
+
+ agt = AG_GET_ARG_AGTYPE_P(0);
+
+ r = palloc(sizeof(agtype_value));
+
+ it = agtype_iterator_init(&agt->root);
+ while ((tok = agtype_iterator_next(&it, r, false)) != WAGT_DONE)
+ {
+ if (IS_A_AGTYPE_SCALAR(r) && AGTYPE_ITERATOR_TOKEN_IS_HASHABLE(tok))
+ agtype_hash_scalar_value_extended(r, &hash, seed);
+ else if (tok == WAGT_BEGIN_ARRAY && !r->val.array.raw_scalar)
+ seed = LEFT_ROTATE(seed, 4);
+ else if (tok == WAGT_BEGIN_OBJECT)
+ seed = LEFT_ROTATE(seed, 6);
+ else if (tok == WAGT_END_ARRAY && !r->val.array.raw_scalar)
+ seed = RIGHT_ROTATE(seed, 4);
+ else if (tok == WAGT_END_OBJECT)
+ seed = RIGHT_ROTATE(seed, 4);
+
+ seed = LEFT_ROTATE(seed, 1);
+ }
+
+ PG_RETURN_INT16(hash);
+}
+
+// Comparision function for btree Indexes
+PG_FUNCTION_INFO_V1(agtype_btree_cmp);
+
+Datum agtype_btree_cmp(PG_FUNCTION_ARGS)
+{
+ agtype *agtype_lhs;
+ agtype *agtype_rhs;
+
+ if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
+ PG_RETURN_INT16(0);
+ else if (PG_ARGISNULL(0))
+ PG_RETURN_INT16(1);
+ else if (PG_ARGISNULL(1))
+ PG_RETURN_INT16(-1);
+
+ agtype_lhs = AG_GET_ARG_AGTYPE_P(0);
+ agtype_rhs = AG_GET_ARG_AGTYPE_P(1);
+
+ PG_RETURN_INT16(compare_agtype_containers_orderability(&agtype_lhs->root,
+ &agtype_rhs->root));
+}
+
+
PG_FUNCTION_INFO_V1(agtype_typecast_numeric);
/*
* Execute function to typecast an agtype to an agtype numeric
@@ -3154,7 +3217,7 @@ Datum _ag_enforce_edge_uniqueness(PG_FUNCTION_ARGS)
}
/* helper function to retrieve a value, given a key, from an agtype_value */
-static agtype_value *get_agtype_value_object_value(agtype_value *agtv_object,
+agtype_value *get_agtype_value_object_value(const agtype_value *agtv_object,
char *key)
{
int i;
@@ -3238,7 +3301,6 @@ Datum id(PG_FUNCTION_ARGS)
Assert(agtv_result->type = AGTV_INTEGER);
PG_RETURN_POINTER(agtype_value_to_agtype(agtv_result));
- AG_RETURN_GRAPHID(agtv_result->val.int_value);
}
PG_FUNCTION_INFO_V1(start_id);
diff --git a/src/backend/utils/adt/agtype_util.c b/src/backend/utils/adt/agtype_util.c
index bf50265..2d32f52 100644
--- a/src/backend/utils/adt/agtype_util.c
+++ b/src/backend/utils/adt/agtype_util.c
@@ -272,6 +272,7 @@ int compare_agtype_containers_orderability(agtype_container *a,
case AGTV_FLOAT:
case AGTV_EDGE:
case AGTV_VERTEX:
+ case AGTV_PATH:
res = compare_agtype_scalar_values(&va, &vb);
break;
case AGTV_ARRAY:
@@ -304,8 +305,6 @@ int compare_agtype_containers_orderability(agtype_container *a,
}
}
break;
- case AGTV_PATH:
- break;
case AGTV_OBJECT:
break;
case AGTV_BINARY:
@@ -1372,7 +1371,7 @@ void agtype_hash_scalar_value(const agtype_value *scalar_val, uint32 *hash)
void agtype_hash_scalar_value_extended(const agtype_value *scalar_val,
uint64 *hash, uint64 seed)
{
- uint64 tmp;
+ uint64 tmp = 0;
switch (scalar_val->type)
{
@@ -1414,17 +1413,30 @@ void agtype_hash_scalar_value_extended(const agtype_value *scalar_val,
case AGTV_VERTEX:
{
graphid id;
- id = scalar_val->val.object.pairs[0].value.val.int_value;
+ agtype_value *id_agt = get_agtype_value_object_value(scalar_val, "id");
+ id = id_agt->val.int_value;
tmp = DatumGetUInt64(DirectFunctionCall2(
- hashfloat8extended, Float8GetDatum(id), UInt64GetDatum(seed)));
+ hashint8extended, Float8GetDatum(id), UInt64GetDatum(seed)));
break;
}
case AGTV_EDGE:
{
graphid id;
- id = scalar_val->val.object.pairs[0].value.val.int_value;
+ agtype_value *id_agt = get_agtype_value_object_value(scalar_val, "id");
+ id = id_agt->val.int_value;
tmp = DatumGetUInt64(DirectFunctionCall2(
- hashfloat8extended, Float8GetDatum(id), UInt64GetDatum(seed)));
+ hashint8extended, Float8GetDatum(id), UInt64GetDatum(seed)));
+ break;
+ }
+ case AGTV_PATH:
+ {
+ int i;
+ for (i = 0; i < scalar_val->val.array.num_elems; i++)
+ {
+ agtype_value v;
+ v = scalar_val->val.array.elems[i];
+ agtype_hash_scalar_value_extended(&v, &tmp, seed);
+ }
break;
}
default:
@@ -1562,10 +1574,16 @@ int compare_agtype_scalar_values(agtype_value *a, agtype_value *b)
return compare_two_floats_orderability(a->val.float_value,
b->val.float_value);
case AGTV_VERTEX:
+ case AGTV_EDGE:
{
+ agtype_value *a_id, *b_id;
graphid a_graphid, b_graphid;
- a_graphid = a->val.object.pairs[0].value.val.int_value;
- b_graphid = b->val.object.pairs[0].value.val.int_value;
+
+ a_id = get_agtype_value_object_value(a, "id");
+ b_id = get_agtype_value_object_value(b, "id");
+
+ a_graphid = a_id->val.int_value;
+ b_graphid = b_id->val.int_value;
if (a_graphid == b_graphid)
return 0;
@@ -1574,6 +1592,29 @@ int compare_agtype_scalar_values(agtype_value *a, agtype_value *b)
else
return -1;
}
+ case AGTV_PATH:
+ {
+ int i;
+
+ if (a->val.array.num_elems != b->val.array.num_elems)
+ return a->val.array.num_elems > b->val.array.num_elems ? 1 : -1;
+
+ for (i = 0; i < a->val.array.num_elems; i++)
+ {
+ agtype_value a_elem, b_elem;
+ int res;
+
+ a_elem = a->val.array.elems[i];
+ b_elem = b->val.array.elems[i];
+
+ res = compare_agtype_scalar_values(&a_elem, &b_elem);
+
+ if (res)
+ return res;
+ }
+
+ return 0;
+ }
default:
ereport(ERROR, (errmsg("invalid agtype scalar type %d for compare",
a->type)));
diff --git a/src/include/utils/agtype.h b/src/include/utils/agtype.h
index a6db4b8..0ec7fb8 100644
--- a/src/include/utils/agtype.h
+++ b/src/include/utils/agtype.h
@@ -45,6 +45,9 @@ typedef enum
WAGT_END_OBJECT
} agtype_iterator_token;
+#define AGTYPE_ITERATOR_TOKEN_IS_HASHABLE(x) \
+ (x > WAGT_DONE && x < WAGT_BEGIN_ARRAY)
+
/* Strategy numbers for GIN index opclasses */
#define AGTYPE_CONTAINS_STRATEGY_NUMBER 7
#define AGTYPE_EXISTS_STRATEGY_NUMBER 9
@@ -442,6 +445,8 @@ Datum get_numeric_datum_from_agtype_value(agtype_value *agtv);
bool is_numeric_result(agtype_value *lhs, agtype_value *rhs);
/* agtype.c support functions */
+agtype_value *get_agtype_value_object_value(const agtype_value *agtv_object,
+ char *key);
char *agtype_to_cstring(StringInfo out, agtype_container *in,
int estimated_len);
char *agtype_to_cstring_indent(StringInfo out, agtype_container *in,