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/06 15:39:02 UTC

[incubator-age] branch master updated: Improve Performance of MATCH and support Indices in Queries

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 57e11a3  Improve Performance of MATCH and support Indices in Queries
57e11a3 is described below

commit 57e11a3e6952d0dcfa50de3b92e07fb596df68b0
Author: Josh Innis <Jo...@gmail.com>
AuthorDate: Wed Apr 6 08:31:54 2022 -0700

    Improve Performance of MATCH and support Indices in Queries
    
    Change the logic that constructs the join tree in a match to  use the id,
    start_id, and end_ids as the graphid type, rather than using Agtype, this will
    save time for every row qual check by skipping unneeded processing time to
    convert graphids to agtype when it is not necessary.
    
    Change the functions used by match to check that the output is  correct will now
    accept graphids and agtype, which will improve performance.
    
    Enable graphids to be used in hash joins and merge joins, this will allow the
    optimizer to choose more efficient query plans in the planner/optimization
    phase.
    
    Fix a bug in the cypher writing clauses, that allow them to support updating
    indices. This allows users to build indices that would allow for more efficient
    queries. Such as using Index scans instead of sequence scans and make for more
    opportunities for merge joins to be a better option for the planner, which
    combines the time complexity of hash joins, with the space complexity of nested
    loop joins. Nested loop joins also have the opportunity of using index scans
    which will also improve performance.
---
 Makefile                             |   1 +
 age--1.0.0.sql                       |  23 ++-
 regress/expected/cypher_delete.out   |   2 +-
 regress/expected/cypher_match.out    |   6 +-
 regress/expected/cypher_merge.out    |   4 +-
 regress/expected/cypher_set.out      |  32 ++--
 regress/expected/cypher_vle.out      |  34 ++--
 regress/expected/expr.out            |  24 +--
 regress/expected/index.out           | 357 +++++++++++++++++++++++++++++++++++
 regress/sql/age_load.sql             |   1 -
 regress/sql/index.sql                | 207 ++++++++++++++++++++
 src/backend/catalog/ag_label.c       |   6 +-
 src/backend/executor/cypher_delete.c |   6 +-
 src/backend/executor/cypher_set.c    |   1 +
 src/backend/executor/cypher_utils.c  |  21 ++-
 src/backend/parser/cypher_clause.c   |  82 ++++----
 src/backend/parser/cypher_expr.c     |   1 -
 src/backend/utils/adt/age_vle.c      | 321 +++++++++++++++++++------------
 src/backend/utils/adt/graphid.c      |  11 ++
 src/include/executor/cypher_utils.h  |   7 +-
 20 files changed, 915 insertions(+), 232 deletions(-)

diff --git a/Makefile b/Makefile
index f64f72a..ad92302 100644
--- a/Makefile
+++ b/Makefile
@@ -88,6 +88,7 @@ REGRESS = scan \
           cypher_union \
           cypher_merge \
           age_load \
+          index \
           drop
 
 srcdir=`pwd`
diff --git a/age--1.0.0.sql b/age--1.0.0.sql
index 2a6e717..bf4ac9a 100644
--- a/age--1.0.0.sql
+++ b/age--1.0.0.sql
@@ -193,7 +193,9 @@ CREATE OPERATOR = (
   COMMUTATOR = =,
   NEGATOR = <>,
   RESTRICT = eqsel,
-  JOIN = eqjoinsel
+  JOIN = eqjoinsel,
+  HASHES,
+  MERGES
 );
 
 CREATE FUNCTION ag_catalog.graphid_ne(graphid, graphid)
@@ -1375,6 +1377,21 @@ CREATE OPERATOR ^ (
   RIGHTARG = agtype
 );
 
+
+CREATE FUNCTION ag_catalog.graphid_hash_cmp(graphid)
+RETURNS INTEGER
+LANGUAGE c
+STABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE OPERATOR CLASS graphid_ops_hash
+  DEFAULT
+  FOR TYPE graphid
+  USING hash AS
+  OPERATOR 1 =,
+  FUNCTION 1 ag_catalog.graphid_hash_cmp(graphid);
+
 --
 -- agtype - comparison operators (=, <>, <, >, <=, >=)
 --
@@ -3828,7 +3845,7 @@ PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
 -- function to match a terminal vle edge
-CREATE FUNCTION ag_catalog.age_match_vle_terminal_edge(agtype, agtype, agtype)
+CREATE FUNCTION ag_catalog.age_match_vle_terminal_edge(variadic "any")
 RETURNS boolean
 LANGUAGE C
 STABLE
@@ -3855,7 +3872,7 @@ RETURNS NULL ON NULL INPUT
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
-CREATE FUNCTION ag_catalog.age_match_vle_edge_to_id_qual(agtype, agtype, agtype)
+CREATE FUNCTION ag_catalog.age_match_vle_edge_to_id_qual(variadic "any")
 RETURNS boolean
 LANGUAGE C
 STABLE
diff --git a/regress/expected/cypher_delete.out b/regress/expected/cypher_delete.out
index 6965cd6..8760a48 100644
--- a/regress/expected/cypher_delete.out
+++ b/regress/expected/cypher_delete.out
@@ -258,8 +258,8 @@ SELECT * FROM cypher('cypher_delete', $$CREATE (n:v)-[:e]->(:v), (n)-[:e]->(:v)
 SELECT * FROM cypher('cypher_delete', $$MATCH(n1)-[e]->() DETACH DELETE n1 RETURN n1, e$$) AS (a agtype, b agtype);
                                 a                                |                                                           b                                                            
 -----------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------
- {"id": 844424930131992, "label": "v", "properties": {}}::vertex | {"id": 1125899906842636, "label": "e", "end_id": 844424930131994, "start_id": 844424930131992, "properties": {}}::edge
  {"id": 844424930131992, "label": "v", "properties": {}}::vertex | {"id": 1125899906842635, "label": "e", "end_id": 844424930131993, "start_id": 844424930131992, "properties": {}}::edge
+ {"id": 844424930131992, "label": "v", "properties": {}}::vertex | {"id": 1125899906842636, "label": "e", "end_id": 844424930131994, "start_id": 844424930131992, "properties": {}}::edge
 (2 rows)
 
 --Cleanup
diff --git a/regress/expected/cypher_match.out b/regress/expected/cypher_match.out
index cf9ff79..febdf36 100644
--- a/regress/expected/cypher_match.out
+++ b/regress/expected/cypher_match.out
@@ -88,8 +88,8 @@ SELECT * FROM cypher('cypher_match', $$
 $$) AS (a agtype);
                                         a                                         
 ----------------------------------------------------------------------------------
- {"id": 1125899906842627, "label": "v1", "properties": {"id": "end"}}::vertex
  {"id": 1125899906842625, "label": "v1", "properties": {"id": "initial"}}::vertex
+ {"id": 1125899906842627, "label": "v1", "properties": {"id": "end"}}::vertex
 (2 rows)
 
 SELECT * FROM cypher('cypher_match', $$
@@ -132,10 +132,10 @@ SELECT * FROM cypher('cypher_match', $$
 $$) AS (a agtype);
                                                              a                                                             
 ---------------------------------------------------------------------------------------------------------------------------
- {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge
- {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge
  {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge
  {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge
+ {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge
+ {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge
 (4 rows)
 
 -- Right Path Test
diff --git a/regress/expected/cypher_merge.out b/regress/expected/cypher_merge.out
index d9841e2..7cf8a45 100644
--- a/regress/expected/cypher_merge.out
+++ b/regress/expected/cypher_merge.out
@@ -637,9 +637,9 @@ SELECT * FROM cypher('cypher_merge', $$
 $$) AS (name agtype, bornIn agtype, city agtype);
        name        |    bornin    |                                          city                                           
 -------------------+--------------+-----------------------------------------------------------------------------------------
- "Michael Douglas" | "New Jersey" | {"id": 1970324836974594, "label": "City", "properties": {"name": "New Jersey"}}::vertex
- "Martin Sheen"    | "Ohio"       | {"id": 1970324836974595, "label": "City", "properties": {"name": "Ohio"}}::vertex
  "Rob Reiner"      | "New York"   | {"id": 1970324836974593, "label": "City", "properties": {"name": "New York"}}::vertex
+ "Martin Sheen"    | "Ohio"       | {"id": 1970324836974595, "label": "City", "properties": {"name": "Ohio"}}::vertex
+ "Michael Douglas" | "New Jersey" | {"id": 1970324836974594, "label": "City", "properties": {"name": "New Jersey"}}::vertex
 (3 rows)
 
 --validate
diff --git a/regress/expected/cypher_set.out b/regress/expected/cypher_set.out
index aa5d8a6..45e9ff7 100644
--- a/regress/expected/cypher_set.out
+++ b/regress/expected/cypher_set.out
@@ -230,12 +230,12 @@ SELECT * FROM cypher('cypher_set', $$MATCH (n)-[]->(n) SET n.y = 99 RETURN n$$)
 SELECT * FROM cypher('cypher_set', $$MATCH (n) MATCH (n)-[]->(m) SET n.t = 150 RETURN n$$) AS (a agtype);
                                                          a                                                          
 --------------------------------------------------------------------------------------------------------------------
- {"id": 281474976710657, "label": "", "properties": {"i": 50, "t": 150}}::vertex
  {"id": 281474976710658, "label": "", "properties": {"t": 150, "y": 1}}::vertex
+ {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 50, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"i": 50, "t": 150}}::vertex
  {"id": 844424930131969, "label": "v", "properties": {"i": 50, "t": 150}}::vertex
  {"id": 844424930131971, "label": "v", "properties": {"i": 50, "t": 150}}::vertex
  {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 50, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
- {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 50, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
 (6 rows)
 
 -- prepared statements
@@ -244,11 +244,11 @@ EXECUTE p_1;
                                                          a                                                         
 -------------------------------------------------------------------------------------------------------------------
  {"id": 281474976710659, "label": "", "properties": {"i": 3, "y": 2}}::vertex
- {"id": 281474976710657, "label": "", "properties": {"i": 3, "t": 150}}::vertex
  {"id": 281474976710658, "label": "", "properties": {"i": 3, "t": 150, "y": 1}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"i": 3, "t": 150}}::vertex
+ {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 3, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
  {"id": 844424930131969, "label": "v", "properties": {"i": 3, "t": 150}}::vertex
  {"id": 844424930131971, "label": "v", "properties": {"i": 3, "t": 150}}::vertex
- {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 3, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
  {"id": 1407374883553281, "label": "other_v", "properties": {"i": 3, "k": 10}}::vertex
  {"id": 1407374883553282, "label": "other_v", "properties": {"i": 3, "k": 10}}::vertex
  {"id": 1407374883553283, "label": "other_v", "properties": {"i": 3, "k": 10}}::vertex
@@ -259,11 +259,11 @@ EXECUTE p_1;
                                                          a                                                         
 -------------------------------------------------------------------------------------------------------------------
  {"id": 281474976710659, "label": "", "properties": {"i": 3, "y": 2}}::vertex
- {"id": 281474976710657, "label": "", "properties": {"i": 3, "t": 150}}::vertex
  {"id": 281474976710658, "label": "", "properties": {"i": 3, "t": 150, "y": 1}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"i": 3, "t": 150}}::vertex
+ {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 3, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
  {"id": 844424930131969, "label": "v", "properties": {"i": 3, "t": 150}}::vertex
  {"id": 844424930131971, "label": "v", "properties": {"i": 3, "t": 150}}::vertex
- {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 3, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
  {"id": 1407374883553281, "label": "other_v", "properties": {"i": 3, "k": 10}}::vertex
  {"id": 1407374883553282, "label": "other_v", "properties": {"i": 3, "k": 10}}::vertex
  {"id": 1407374883553283, "label": "other_v", "properties": {"i": 3, "k": 10}}::vertex
@@ -275,11 +275,11 @@ EXECUTE p_2('{"var_name": 4}');
                                                          a                                                         
 -------------------------------------------------------------------------------------------------------------------
  {"id": 281474976710659, "label": "", "properties": {"i": 4, "y": 2}}::vertex
- {"id": 281474976710657, "label": "", "properties": {"i": 4, "t": 150}}::vertex
  {"id": 281474976710658, "label": "", "properties": {"i": 4, "t": 150, "y": 1}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"i": 4, "t": 150}}::vertex
+ {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 4, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
  {"id": 844424930131969, "label": "v", "properties": {"i": 4, "t": 150}}::vertex
  {"id": 844424930131971, "label": "v", "properties": {"i": 4, "t": 150}}::vertex
- {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 4, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
  {"id": 1407374883553281, "label": "other_v", "properties": {"i": 4, "k": 10}}::vertex
  {"id": 1407374883553282, "label": "other_v", "properties": {"i": 4, "k": 10}}::vertex
  {"id": 1407374883553283, "label": "other_v", "properties": {"i": 4, "k": 10}}::vertex
@@ -290,11 +290,11 @@ EXECUTE p_2('{"var_name": 6}');
                                                          a                                                         
 -------------------------------------------------------------------------------------------------------------------
  {"id": 281474976710659, "label": "", "properties": {"i": 6, "y": 2}}::vertex
- {"id": 281474976710657, "label": "", "properties": {"i": 6, "t": 150}}::vertex
  {"id": 281474976710658, "label": "", "properties": {"i": 6, "t": 150, "y": 1}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"i": 6, "t": 150}}::vertex
+ {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 6, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
  {"id": 844424930131969, "label": "v", "properties": {"i": 6, "t": 150}}::vertex
  {"id": 844424930131971, "label": "v", "properties": {"i": 6, "t": 150}}::vertex
- {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 6, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
  {"id": 1407374883553281, "label": "other_v", "properties": {"i": 6, "k": 10}}::vertex
  {"id": 1407374883553282, "label": "other_v", "properties": {"i": 6, "k": 10}}::vertex
  {"id": 1407374883553283, "label": "other_v", "properties": {"i": 6, "k": 10}}::vertex
@@ -314,11 +314,11 @@ SELECT set_test();
                                                      set_test                                                      
 -------------------------------------------------------------------------------------------------------------------
  {"id": 281474976710659, "label": "", "properties": {"i": 7, "y": 2}}::vertex
- {"id": 281474976710657, "label": "", "properties": {"i": 7, "t": 150}}::vertex
  {"id": 281474976710658, "label": "", "properties": {"i": 7, "t": 150, "y": 1}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"i": 7, "t": 150}}::vertex
+ {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 7, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
  {"id": 844424930131969, "label": "v", "properties": {"i": 7, "t": 150}}::vertex
  {"id": 844424930131971, "label": "v", "properties": {"i": 7, "t": 150}}::vertex
- {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 7, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
  {"id": 1407374883553281, "label": "other_v", "properties": {"i": 7, "k": 10}}::vertex
  {"id": 1407374883553282, "label": "other_v", "properties": {"i": 7, "k": 10}}::vertex
  {"id": 1407374883553283, "label": "other_v", "properties": {"i": 7, "k": 10}}::vertex
@@ -329,11 +329,11 @@ SELECT set_test();
                                                      set_test                                                      
 -------------------------------------------------------------------------------------------------------------------
  {"id": 281474976710659, "label": "", "properties": {"i": 7, "y": 2}}::vertex
- {"id": 281474976710657, "label": "", "properties": {"i": 7, "t": 150}}::vertex
  {"id": 281474976710658, "label": "", "properties": {"i": 7, "t": 150, "y": 1}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"i": 7, "t": 150}}::vertex
+ {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 7, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
  {"id": 844424930131969, "label": "v", "properties": {"i": 7, "t": 150}}::vertex
  {"id": 844424930131971, "label": "v", "properties": {"i": 7, "t": 150}}::vertex
- {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 7, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
  {"id": 1407374883553281, "label": "other_v", "properties": {"i": 7, "k": 10}}::vertex
  {"id": 1407374883553282, "label": "other_v", "properties": {"i": 7, "k": 10}}::vertex
  {"id": 1407374883553283, "label": "other_v", "properties": {"i": 7, "k": 10}}::vertex
@@ -347,11 +347,11 @@ SELECT * FROM cypher('cypher_set', $$MATCH (n) SET n.i = 3, n.j = 5 RETURN n $$)
                                                          a                                                         
 -------------------------------------------------------------------------------------------------------------------
  {"id": 281474976710659, "label": "", "properties": {"i": 3, "j": 5, "y": 2}}::vertex
- {"id": 281474976710657, "label": "", "properties": {"i": 3, "j": 5, "t": 150}}::vertex
  {"id": 281474976710658, "label": "", "properties": {"i": 3, "j": 5, "t": 150, "y": 1}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"i": 3, "j": 5, "t": 150}}::vertex
+ {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 3, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
  {"id": 844424930131969, "label": "v", "properties": {"i": 3, "j": 5, "t": 150}}::vertex
  {"id": 844424930131971, "label": "v", "properties": {"i": 3, "j": 5, "t": 150}}::vertex
- {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 3, "j": 5, "t": 150, "y": 99, "z": 99}}::vertex
  {"id": 1407374883553281, "label": "other_v", "properties": {"i": 3, "j": 5, "k": 10}}::vertex
  {"id": 1407374883553282, "label": "other_v", "properties": {"i": 3, "j": 5, "k": 10}}::vertex
  {"id": 1407374883553283, "label": "other_v", "properties": {"i": 3, "j": 5, "k": 10}}::vertex
diff --git a/regress/expected/cypher_vle.out b/regress/expected/cypher_vle.out
index 3bf7086..7fbef33 100644
--- a/regress/expected/cypher_vle.out
+++ b/regress/expected/cypher_vle.out
@@ -508,37 +508,37 @@ SELECT * FROM cypher('cypher_vle', $$MATCH p=(u)-[e*0..0]->(v) RETURN id(u), p,
 SELECT * FROM cypher('cypher_vle', $$MATCH p=()-[*0..0]->()-[]->() RETURN p $$) AS (p agtype);
                                                                                                                                                                                                      p                                                                                                                                                                                                     
 -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- [{"id": 1407374883553283, "label": "middle", "properties": {}}::vertex, {"id": 1125899906842625, "label": "edge", "end_id": 1688849860263937, "start_id": 1407374883553283, "properties": {"name": "main edge", "number": 4, "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1688849860263937, "label": "end", "properties": {}}::vertex]::path
- [{"id": 1407374883553282, "label": "middle", "properties": {}}::vertex, {"id": 1125899906842626, "label": "edge", "end_id": 1407374883553283, "start_id": 1407374883553282, "properties": {"name": "main edge", "number": 3, "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1407374883553283, "label": "middle", "properties": {}}::vertex]::path
- [{"id": 1407374883553281, "label": "middle", "properties": {}}::vertex, {"id": 1125899906842627, "label": "edge", "end_id": 1407374883553282, "start_id": 1407374883553281, "properties": {"name": "main edge", "number": 2, "packages": [2, 4, 6], "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1407374883553282, "label": "middle", "properties": {}}::vertex]::path
+ [{"id": 844424930131969, "label": "begin", "properties": {}}::vertex, {"id": 2251799813685249, "label": "alternate_edge", "end_id": 1407374883553281, "start_id": 844424930131969, "properties": {"name": "alternate edge", "number": 1, "packages": [2, 4, 6], "dangerous": {"type": "poisons", "level": "all"}}}::edge, {"id": 1407374883553281, "label": "middle", "properties": {}}::vertex]::path
  [{"id": 844424930131969, "label": "begin", "properties": {}}::vertex, {"id": 1125899906842628, "label": "edge", "end_id": 1407374883553281, "start_id": 844424930131969, "properties": {"name": "main edge", "number": 1, "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1407374883553281, "label": "middle", "properties": {}}::vertex]::path
+ [{"id": 1407374883553281, "label": "middle", "properties": {}}::vertex, {"id": 1125899906842627, "label": "edge", "end_id": 1407374883553282, "start_id": 1407374883553281, "properties": {"name": "main edge", "number": 2, "packages": [2, 4, 6], "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1407374883553282, "label": "middle", "properties": {}}::vertex]::path
  [{"id": 1407374883553281, "label": "middle", "properties": {}}::vertex, {"id": 1970324836974593, "label": "self_loop", "end_id": 1407374883553281, "start_id": 1407374883553281, "properties": {"name": "self loop", "number": 1, "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1407374883553281, "label": "middle", "properties": {}}::vertex]::path
- [{"id": 1688849860263937, "label": "end", "properties": {}}::vertex, {"id": 1970324836974594, "label": "self_loop", "end_id": 1688849860263937, "start_id": 1688849860263937, "properties": {"name": "self loop", "number": 2, "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1688849860263937, "label": "end", "properties": {}}::vertex]::path
- [{"id": 844424930131969, "label": "begin", "properties": {}}::vertex, {"id": 2251799813685249, "label": "alternate_edge", "end_id": 1407374883553281, "start_id": 844424930131969, "properties": {"name": "alternate edge", "number": 1, "packages": [2, 4, 6], "dangerous": {"type": "poisons", "level": "all"}}}::edge, {"id": 1407374883553281, "label": "middle", "properties": {}}::vertex]::path
+ [{"id": 1407374883553282, "label": "middle", "properties": {}}::vertex, {"id": 1125899906842626, "label": "edge", "end_id": 1407374883553283, "start_id": 1407374883553282, "properties": {"name": "main edge", "number": 3, "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1407374883553283, "label": "middle", "properties": {}}::vertex]::path
  [{"id": 1407374883553282, "label": "middle", "properties": {}}::vertex, {"id": 2251799813685250, "label": "alternate_edge", "end_id": 1407374883553283, "start_id": 1407374883553282, "properties": {"name": "alternate edge", "number": 2, "packages": [2, 4, 6], "dangerous": {"type": "poisons", "level": "all"}}}::edge, {"id": 1407374883553283, "label": "middle", "properties": {}}::vertex]::path
- [{"id": 1407374883553283, "label": "middle", "properties": {}}::vertex, {"id": 2251799813685251, "label": "alternate_edge", "end_id": 1688849860263937, "start_id": 1407374883553283, "properties": {"name": "alternate edge", "number": 3, "packages": [2, 4, 6], "dangerous": {"type": "poisons", "level": "all"}}}::edge, {"id": 1688849860263937, "label": "end", "properties": {}}::vertex]::path
- [{"id": 1688849860263937, "label": "end", "properties": {}}::vertex, {"id": 2251799813685252, "label": "alternate_edge", "end_id": 1407374883553283, "start_id": 1688849860263937, "properties": {"name": "backup edge", "number": 1, "packages": [1, 3, 5, 7]}}::edge, {"id": 1407374883553283, "label": "middle", "properties": {}}::vertex]::path
- [{"id": 1407374883553283, "label": "middle", "properties": {}}::vertex, {"id": 2251799813685253, "label": "alternate_edge", "end_id": 1407374883553282, "start_id": 1407374883553283, "properties": {"name": "backup edge", "number": 2, "packages": [1, 3, 5, 7]}}::edge, {"id": 1407374883553282, "label": "middle", "properties": {}}::vertex]::path
  [{"id": 1407374883553282, "label": "middle", "properties": {}}::vertex, {"id": 2533274790395905, "label": "bypass_edge", "end_id": 1688849860263937, "start_id": 1407374883553282, "properties": {"name": "bypass edge", "number": 1, "packages": [1, 3, 5, 7]}}::edge, {"id": 1688849860263937, "label": "end", "properties": {}}::vertex]::path
  [{"id": 1407374883553282, "label": "middle", "properties": {}}::vertex, {"id": 2533274790395906, "label": "bypass_edge", "end_id": 844424930131969, "start_id": 1407374883553282, "properties": {"name": "bypass edge", "number": 2, "packages": [1, 3, 5, 7], "dangerous": {"type": "poisons", "level": "all"}}}::edge, {"id": 844424930131969, "label": "begin", "properties": {}}::vertex]::path
+ [{"id": 1407374883553283, "label": "middle", "properties": {}}::vertex, {"id": 1125899906842625, "label": "edge", "end_id": 1688849860263937, "start_id": 1407374883553283, "properties": {"name": "main edge", "number": 4, "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1688849860263937, "label": "end", "properties": {}}::vertex]::path
+ [{"id": 1407374883553283, "label": "middle", "properties": {}}::vertex, {"id": 2251799813685253, "label": "alternate_edge", "end_id": 1407374883553282, "start_id": 1407374883553283, "properties": {"name": "backup edge", "number": 2, "packages": [1, 3, 5, 7]}}::edge, {"id": 1407374883553282, "label": "middle", "properties": {}}::vertex]::path
+ [{"id": 1407374883553283, "label": "middle", "properties": {}}::vertex, {"id": 2251799813685251, "label": "alternate_edge", "end_id": 1688849860263937, "start_id": 1407374883553283, "properties": {"name": "alternate edge", "number": 3, "packages": [2, 4, 6], "dangerous": {"type": "poisons", "level": "all"}}}::edge, {"id": 1688849860263937, "label": "end", "properties": {}}::vertex]::path
+ [{"id": 1688849860263937, "label": "end", "properties": {}}::vertex, {"id": 2251799813685252, "label": "alternate_edge", "end_id": 1407374883553283, "start_id": 1688849860263937, "properties": {"name": "backup edge", "number": 1, "packages": [1, 3, 5, 7]}}::edge, {"id": 1407374883553283, "label": "middle", "properties": {}}::vertex]::path
+ [{"id": 1688849860263937, "label": "end", "properties": {}}::vertex, {"id": 1970324836974594, "label": "self_loop", "end_id": 1688849860263937, "start_id": 1688849860263937, "properties": {"name": "self loop", "number": 2, "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1688849860263937, "label": "end", "properties": {}}::vertex]::path
 (13 rows)
 
 SELECT * FROM cypher('cypher_vle', $$MATCH p=()-[]->()-[*0..0]->() RETURN p $$) AS (p agtype);
                                                                                                                                                                                                      p                                                                                                                                                                                                     
 -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- [{"id": 1407374883553283, "label": "middle", "properties": {}}::vertex, {"id": 1125899906842625, "label": "edge", "end_id": 1688849860263937, "start_id": 1407374883553283, "properties": {"name": "main edge", "number": 4, "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1688849860263937, "label": "end", "properties": {}}::vertex]::path
- [{"id": 1407374883553282, "label": "middle", "properties": {}}::vertex, {"id": 1125899906842626, "label": "edge", "end_id": 1407374883553283, "start_id": 1407374883553282, "properties": {"name": "main edge", "number": 3, "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1407374883553283, "label": "middle", "properties": {}}::vertex]::path
- [{"id": 1407374883553281, "label": "middle", "properties": {}}::vertex, {"id": 1125899906842627, "label": "edge", "end_id": 1407374883553282, "start_id": 1407374883553281, "properties": {"name": "main edge", "number": 2, "packages": [2, 4, 6], "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1407374883553282, "label": "middle", "properties": {}}::vertex]::path
+ [{"id": 1407374883553282, "label": "middle", "properties": {}}::vertex, {"id": 2533274790395906, "label": "bypass_edge", "end_id": 844424930131969, "start_id": 1407374883553282, "properties": {"name": "bypass edge", "number": 2, "packages": [1, 3, 5, 7], "dangerous": {"type": "poisons", "level": "all"}}}::edge, {"id": 844424930131969, "label": "begin", "properties": {}}::vertex]::path
+ [{"id": 844424930131969, "label": "begin", "properties": {}}::vertex, {"id": 2251799813685249, "label": "alternate_edge", "end_id": 1407374883553281, "start_id": 844424930131969, "properties": {"name": "alternate edge", "number": 1, "packages": [2, 4, 6], "dangerous": {"type": "poisons", "level": "all"}}}::edge, {"id": 1407374883553281, "label": "middle", "properties": {}}::vertex]::path
  [{"id": 844424930131969, "label": "begin", "properties": {}}::vertex, {"id": 1125899906842628, "label": "edge", "end_id": 1407374883553281, "start_id": 844424930131969, "properties": {"name": "main edge", "number": 1, "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1407374883553281, "label": "middle", "properties": {}}::vertex]::path
  [{"id": 1407374883553281, "label": "middle", "properties": {}}::vertex, {"id": 1970324836974593, "label": "self_loop", "end_id": 1407374883553281, "start_id": 1407374883553281, "properties": {"name": "self loop", "number": 1, "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1407374883553281, "label": "middle", "properties": {}}::vertex]::path
- [{"id": 1688849860263937, "label": "end", "properties": {}}::vertex, {"id": 1970324836974594, "label": "self_loop", "end_id": 1688849860263937, "start_id": 1688849860263937, "properties": {"name": "self loop", "number": 2, "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1688849860263937, "label": "end", "properties": {}}::vertex]::path
- [{"id": 844424930131969, "label": "begin", "properties": {}}::vertex, {"id": 2251799813685249, "label": "alternate_edge", "end_id": 1407374883553281, "start_id": 844424930131969, "properties": {"name": "alternate edge", "number": 1, "packages": [2, 4, 6], "dangerous": {"type": "poisons", "level": "all"}}}::edge, {"id": 1407374883553281, "label": "middle", "properties": {}}::vertex]::path
- [{"id": 1407374883553282, "label": "middle", "properties": {}}::vertex, {"id": 2251799813685250, "label": "alternate_edge", "end_id": 1407374883553283, "start_id": 1407374883553282, "properties": {"name": "alternate edge", "number": 2, "packages": [2, 4, 6], "dangerous": {"type": "poisons", "level": "all"}}}::edge, {"id": 1407374883553283, "label": "middle", "properties": {}}::vertex]::path
- [{"id": 1407374883553283, "label": "middle", "properties": {}}::vertex, {"id": 2251799813685251, "label": "alternate_edge", "end_id": 1688849860263937, "start_id": 1407374883553283, "properties": {"name": "alternate edge", "number": 3, "packages": [2, 4, 6], "dangerous": {"type": "poisons", "level": "all"}}}::edge, {"id": 1688849860263937, "label": "end", "properties": {}}::vertex]::path
- [{"id": 1688849860263937, "label": "end", "properties": {}}::vertex, {"id": 2251799813685252, "label": "alternate_edge", "end_id": 1407374883553283, "start_id": 1688849860263937, "properties": {"name": "backup edge", "number": 1, "packages": [1, 3, 5, 7]}}::edge, {"id": 1407374883553283, "label": "middle", "properties": {}}::vertex]::path
  [{"id": 1407374883553283, "label": "middle", "properties": {}}::vertex, {"id": 2251799813685253, "label": "alternate_edge", "end_id": 1407374883553282, "start_id": 1407374883553283, "properties": {"name": "backup edge", "number": 2, "packages": [1, 3, 5, 7]}}::edge, {"id": 1407374883553282, "label": "middle", "properties": {}}::vertex]::path
+ [{"id": 1407374883553281, "label": "middle", "properties": {}}::vertex, {"id": 1125899906842627, "label": "edge", "end_id": 1407374883553282, "start_id": 1407374883553281, "properties": {"name": "main edge", "number": 2, "packages": [2, 4, 6], "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1407374883553282, "label": "middle", "properties": {}}::vertex]::path
+ [{"id": 1688849860263937, "label": "end", "properties": {}}::vertex, {"id": 2251799813685252, "label": "alternate_edge", "end_id": 1407374883553283, "start_id": 1688849860263937, "properties": {"name": "backup edge", "number": 1, "packages": [1, 3, 5, 7]}}::edge, {"id": 1407374883553283, "label": "middle", "properties": {}}::vertex]::path
+ [{"id": 1407374883553282, "label": "middle", "properties": {}}::vertex, {"id": 1125899906842626, "label": "edge", "end_id": 1407374883553283, "start_id": 1407374883553282, "properties": {"name": "main edge", "number": 3, "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1407374883553283, "label": "middle", "properties": {}}::vertex]::path
+ [{"id": 1407374883553282, "label": "middle", "properties": {}}::vertex, {"id": 2251799813685250, "label": "alternate_edge", "end_id": 1407374883553283, "start_id": 1407374883553282, "properties": {"name": "alternate edge", "number": 2, "packages": [2, 4, 6], "dangerous": {"type": "poisons", "level": "all"}}}::edge, {"id": 1407374883553283, "label": "middle", "properties": {}}::vertex]::path
  [{"id": 1407374883553282, "label": "middle", "properties": {}}::vertex, {"id": 2533274790395905, "label": "bypass_edge", "end_id": 1688849860263937, "start_id": 1407374883553282, "properties": {"name": "bypass edge", "number": 1, "packages": [1, 3, 5, 7]}}::edge, {"id": 1688849860263937, "label": "end", "properties": {}}::vertex]::path
- [{"id": 1407374883553282, "label": "middle", "properties": {}}::vertex, {"id": 2533274790395906, "label": "bypass_edge", "end_id": 844424930131969, "start_id": 1407374883553282, "properties": {"name": "bypass edge", "number": 2, "packages": [1, 3, 5, 7], "dangerous": {"type": "poisons", "level": "all"}}}::edge, {"id": 844424930131969, "label": "begin", "properties": {}}::vertex]::path
+ [{"id": 1688849860263937, "label": "end", "properties": {}}::vertex, {"id": 1970324836974594, "label": "self_loop", "end_id": 1688849860263937, "start_id": 1688849860263937, "properties": {"name": "self loop", "number": 2, "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1688849860263937, "label": "end", "properties": {}}::vertex]::path
+ [{"id": 1407374883553283, "label": "middle", "properties": {}}::vertex, {"id": 1125899906842625, "label": "edge", "end_id": 1688849860263937, "start_id": 1407374883553283, "properties": {"name": "main edge", "number": 4, "dangerous": {"type": "all", "level": "all"}}}::edge, {"id": 1688849860263937, "label": "end", "properties": {}}::vertex]::path
+ [{"id": 1407374883553283, "label": "middle", "properties": {}}::vertex, {"id": 2251799813685251, "label": "alternate_edge", "end_id": 1688849860263937, "start_id": 1407374883553283, "properties": {"name": "alternate edge", "number": 3, "packages": [2, 4, 6], "dangerous": {"type": "poisons", "level": "all"}}}::edge, {"id": 1688849860263937, "label": "end", "properties": {}}::vertex]::path
 (13 rows)
 
 --
diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index f4fdc4b..73dde0a 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -1663,10 +1663,10 @@ SELECT * FROM cypher('expr', $$ MATCH (v) RETURN v $$) AS (expression agtype);
 SELECT * FROM cypher('expr', $$ MATCH ()-[e]-() RETURN e $$) AS (expression agtype);
                                                         expression                                                         
 ---------------------------------------------------------------------------------------------------------------------------
- {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge
- {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge
  {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge
+ {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge
  {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge
+ {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge
 (4 rows)
 
 -- id()
@@ -1675,10 +1675,10 @@ SELECT * FROM cypher('expr', $$
 $$) AS (id agtype);
         id        
 ------------------
- 1407374883553281
- 1407374883553281
  1407374883553282
+ 1407374883553281
  1407374883553282
+ 1407374883553281
 (4 rows)
 
 SELECT * FROM cypher('expr', $$
@@ -1717,10 +1717,10 @@ SELECT * FROM cypher('expr', $$
 $$) AS (start_id agtype);
      start_id     
 ------------------
- 1125899906842626
- 1125899906842626
  1125899906842625
+ 1125899906842626
  1125899906842625
+ 1125899906842626
 (4 rows)
 
 -- should return null
@@ -1750,10 +1750,10 @@ SELECT * FROM cypher('expr', $$
 $$) AS (end_id agtype);
       end_id      
 ------------------
- 1125899906842627
- 1125899906842627
  1125899906842626
+ 1125899906842627
  1125899906842626
+ 1125899906842627
 (4 rows)
 
 -- should return null
@@ -5492,15 +5492,15 @@ SELECT * FROM cypher('opt_forms', $$MATCH (u)--(v) RETURN u.i, v.i$$) AS (u agty
 SELECT * FROM cypher('opt_forms', $$MATCH (u)-->(v) RETURN u.i, v.i$$) AS (u agtype, v agtype);
  u | v 
 ---+---
- 1 | 2
  3 | 2
+ 1 | 2
 (2 rows)
 
 SELECT * FROM cypher('opt_forms', $$MATCH (u)<--(v) RETURN u.i, v.i$$) AS (u agtype, v agtype);
  u | v 
 ---+---
- 2 | 1
  2 | 3
+ 2 | 1
 (2 rows)
 
 SELECT * FROM cypher('opt_forms', $$MATCH (u)-->()<--(v) RETURN u.i, v.i$$) AS (u agtype, v agtype);
@@ -5716,8 +5716,8 @@ SELECT * from cypher('list', $$MATCH p=()-[]->() RETURN nodes(p)$$) as (nodes ag
                                                                                nodes                                                                               
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------
  [{"id": 281474976710657, "label": "", "properties": {"name": "rick"}}::vertex, {"id": 281474976710658, "label": "", "properties": {"name": "morty"}}::vertex]
- [{"id": 281474976710659, "label": "", "properties": {"name": "rachael"}}::vertex, {"id": 281474976710660, "label": "", "properties": {"name": "monica"}}::vertex]
  [{"id": 281474976710660, "label": "", "properties": {"name": "monica"}}::vertex, {"id": 281474976710661, "label": "", "properties": {"name": "phoebe"}}::vertex]
+ [{"id": 281474976710659, "label": "", "properties": {"name": "rachael"}}::vertex, {"id": 281474976710660, "label": "", "properties": {"name": "monica"}}::vertex]
 (3 rows)
 
 SELECT * from cypher('list', $$MATCH p=()-[]->()-[]->() RETURN nodes(p)$$) as (nodes agtype);
@@ -5753,8 +5753,8 @@ SELECT * from cypher('list', $$MATCH p=()-[]->() RETURN relationships(p)$$) as (
                                                         relationships                                                        
 -----------------------------------------------------------------------------------------------------------------------------
  [{"id": 844424930131969, "label": "knows", "end_id": 281474976710658, "start_id": 281474976710657, "properties": {}}::edge]
- [{"id": 844424930131971, "label": "knows", "end_id": 281474976710660, "start_id": 281474976710659, "properties": {}}::edge]
  [{"id": 844424930131970, "label": "knows", "end_id": 281474976710661, "start_id": 281474976710660, "properties": {}}::edge]
+ [{"id": 844424930131971, "label": "knows", "end_id": 281474976710660, "start_id": 281474976710659, "properties": {}}::edge]
 (3 rows)
 
 SELECT * from cypher('list', $$MATCH p=()-[]->()-[]->() RETURN relationships(p)$$) as (relationships agtype);
diff --git a/regress/expected/index.out b/regress/expected/index.out
new file mode 100644
index 0000000..09af778
--- /dev/null
+++ b/regress/expected/index.out
@@ -0,0 +1,357 @@
+\! cp -r regress/age_load/data regress/instance/data/age_load
+LOAD 'age';
+SET search_path TO ag_catalog;
+SET enable_mergejoin = ON;
+SET enable_hashjoin = ON;
+SET enable_nestloop = ON;
+SELECT create_graph('cypher_index');
+NOTICE:  graph "cypher_index" has been created
+ create_graph 
+--------------
+ 
+(1 row)
+
+/*
+ * Section 1: Unique Index on Properties
+ */
+--Section 1 Setup
+SELECT create_vlabel('cypher_index', 'idx');
+NOTICE:  VLabel "idx" has been created
+ create_vlabel 
+---------------
+ 
+(1 row)
+
+CREATE UNIQUE INDEX cypher_index_idx_props_uq ON cypher_index.idx(properties);
+--Test 1
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+ERROR:  duplicate key value violates unique constraint "cypher_index_idx_props_uq"
+DETAIL:  Key (properties)=({"i": 1}) already exists.
+--Clean Up
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+--Test 2
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}), (:idx {i: 1}) $$) AS (a agtype);
+ERROR:  duplicate key value violates unique constraint "cypher_index_idx_props_uq"
+DETAIL:  Key (properties)=({"i": 1}) already exists.
+--Clean Up
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+--Test 3
+--Data Setup
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx) $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+--Query
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) SET n.i = 1$$) AS (a agtype);
+ERROR:  duplicate key value violates unique constraint "cypher_index_idx_props_uq"
+DETAIL:  Key (properties)=({"i": 1}) already exists.
+--Clean Up
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+--Test 4
+--create a vertex with i = 1
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+--delete the vertex
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+--we should be able to create a new vertex with the same value
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+--data cleanup
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+/*
+ * Test 5
+ *
+ * Same queries as Test 4, only in 1 transaction
+ */
+BEGIN TRANSACTION;
+--create a vertex with i = 1
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+--delete the vertex
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+--we should be able to create a new vertex with the same value
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+COMMIT;
+--data cleanup
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+--Test 6
+--create a vertex with i = 1
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+-- change the value
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) SET n.i = 2 $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+--we should be able to create a new vertex with the same value
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+--validate the data
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) RETURN n $$) AS (a agtype);
+                                    a                                    
+-------------------------------------------------------------------------
+ {"id": 844424930131979, "label": "idx", "properties": {"i": 2}}::vertex
+ {"id": 844424930131980, "label": "idx", "properties": {"i": 1}}::vertex
+(2 rows)
+
+--data cleanup
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+/*
+ * Test 7
+ *
+ * Same queries as Test 6, only in 1 transaction
+ */
+BEGIN TRANSACTION;
+--create a vertex with i = 1
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+-- change the value
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) SET n.i = 2 $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+--we should be able to create a new vertex with the same value
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+--validate the data
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) RETURN n $$) AS (a agtype);
+                                    a                                    
+-------------------------------------------------------------------------
+ {"id": 844424930131981, "label": "idx", "properties": {"i": 2}}::vertex
+ {"id": 844424930131982, "label": "idx", "properties": {"i": 1}}::vertex
+(2 rows)
+
+COMMIT;
+--validate the data again out of the transaction, just in case
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) RETURN n $$) AS (a agtype);
+                                    a                                    
+-------------------------------------------------------------------------
+ {"id": 844424930131981, "label": "idx", "properties": {"i": 2}}::vertex
+ {"id": 844424930131982, "label": "idx", "properties": {"i": 1}}::vertex
+(2 rows)
+
+--data cleanup
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+--Test 8
+--create a vertex with i = 1
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+-- Use Merge and force an index error
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) MERGE (n)-[:e]->(:idx {i: n.i}) $$) AS (a agtype);
+ERROR:  duplicate key value violates unique constraint "cypher_index_idx_props_uq"
+DETAIL:  Key (properties)=({"i": 1}) already exists.
+--data cleanup
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+/*
+ * Section 2: Indices to Improve Query Runtime
+ */
+SELECT create_graph('agload_test_graph');
+NOTICE:  graph "agload_test_graph" has been created
+ create_graph 
+--------------
+ 
+(1 row)
+
+ALTER TABLE agload_test_graph._ag_label_vertex
+CLUSTER ON _ag_label_vertex_pkey;
+SELECT create_vlabel('agload_test_graph','Country');
+NOTICE:  VLabel "Country" has been created
+ create_vlabel 
+---------------
+ 
+(1 row)
+
+SELECT load_labels_from_file('agload_test_graph', 'Country',
+    'age_load/countries.csv');
+ load_labels_from_file 
+-----------------------
+ 
+(1 row)
+
+ALTER TABLE agload_test_graph."Country" ADD PRIMARY KEY (id);
+CREATE UNIQUE INDEX CONCURRENTLY cntry_id_idx ON agload_test_graph."Country" (id);
+ALTER TABLE agload_test_graph."Country"  CLUSTER ON cntry_id_idx;
+SELECT create_vlabel('agload_test_graph','City');
+NOTICE:  VLabel "City" has been created
+ create_vlabel 
+---------------
+ 
+(1 row)
+
+SELECT load_labels_from_file('agload_test_graph', 'City',
+    'age_load/cities.csv');
+ load_labels_from_file 
+-----------------------
+ 
+(1 row)
+
+ALTER TABLE agload_test_graph."City"
+ADD PRIMARY KEY (id);
+CREATE UNIQUE INDEX city_id_idx
+ON agload_test_graph."City" (id);
+ALTER TABLE agload_test_graph."City"
+CLUSTER ON city_id_idx;
+SELECT create_elabel('agload_test_graph','has_city');
+NOTICE:  ELabel "has_city" has been created
+ create_elabel 
+---------------
+ 
+(1 row)
+
+SELECT load_edges_from_file('agload_test_graph', 'has_city',
+     'age_load/edges.csv');
+ load_edges_from_file 
+----------------------
+ 
+(1 row)
+
+ALTER TABLE agload_test_graph.has_city
+ADD CONSTRAINT has_city_end_fk FOREIGN KEY (end_id)
+REFERENCES agload_test_graph."Country"(id) MATCH FULL;
+CREATE INDEX load_has_city_eid_idx
+ON agload_test_graph.has_city (end_id);
+CREATE INDEX load_has_city_sid_idx
+ON agload_test_graph.has_city (start_id);
+ALTER TABLE agload_test_graph."has_city"
+CLUSTER ON load_has_city_eid_idx;
+SET enable_mergejoin = ON;
+SET enable_hashjoin = OFF;
+SET enable_nestloop = OFF;
+SELECT COUNT(*) FROM cypher('agload_test_graph', $$
+    MATCH (a:Country)<-[e:has_city]-()
+    RETURN e
+$$) as (n agtype);
+ count 
+-------
+ 72485
+(1 row)
+
+SET enable_mergejoin = OFF;
+SET enable_hashjoin = ON;
+SET enable_nestloop = OFF;
+SELECT COUNT(*) FROM cypher('agload_test_graph', $$
+    MATCH (a:Country)<-[e:has_city]-()
+    RETURN e
+$$) as (n agtype);
+ count 
+-------
+ 72485
+(1 row)
+
+SET enable_mergejoin = OFF;
+SET enable_hashjoin = OFF;
+SET enable_nestloop = ON;
+SELECT COUNT(*) FROM cypher('agload_test_graph', $$
+    MATCH (a:Country)<-[e:has_city]-()
+    RETURN e
+$$) as (n agtype);
+ count 
+-------
+ 72485
+(1 row)
+
+SELECT drop_graph('cypher_index', true);
+NOTICE:  drop cascades to 3 other objects
+DETAIL:  drop cascades to table cypher_index._ag_label_vertex
+drop cascades to table cypher_index._ag_label_edge
+drop cascades to table cypher_index.idx
+NOTICE:  graph "cypher_index" has been dropped
+ drop_graph 
+------------
+ 
+(1 row)
+
+SELECT drop_graph('agload_test_graph', true);
+NOTICE:  drop cascades to 5 other objects
+DETAIL:  drop cascades to table agload_test_graph._ag_label_vertex
+drop cascades to table agload_test_graph._ag_label_edge
+drop cascades to table agload_test_graph."Country"
+drop cascades to table agload_test_graph."City"
+drop cascades to table agload_test_graph.has_city
+NOTICE:  graph "agload_test_graph" has been dropped
+ drop_graph 
+------------
+ 
+(1 row)
+
diff --git a/regress/sql/age_load.sql b/regress/sql/age_load.sql
index fa2da0b..a6008ca 100644
--- a/regress/sql/age_load.sql
+++ b/regress/sql/age_load.sql
@@ -29,7 +29,6 @@ SELECT COUNT(*) FROM cypher('agload_test_graph', $$MATCH(n) RETURN n$$) as (n ag
 
 SELECT COUNT(*) FROM cypher('agload_test_graph', $$MATCH (a)-[e]->(b) RETURN e$$) as (n agtype);
 
-
 SELECT create_vlabel('agload_test_graph','Country2');
 SELECT load_labels_from_file('agload_test_graph', 'Country2',
                              'age_load/countries.csv', false);
diff --git a/regress/sql/index.sql b/regress/sql/index.sql
new file mode 100644
index 0000000..0ec3ab5
--- /dev/null
+++ b/regress/sql/index.sql
@@ -0,0 +1,207 @@
+\! cp -r regress/age_load/data regress/instance/data/age_load
+
+LOAD 'age';
+SET search_path TO ag_catalog;
+
+SET enable_mergejoin = ON;
+SET enable_hashjoin = ON;
+SET enable_nestloop = ON;
+
+SELECT create_graph('cypher_index');
+
+/*
+ * Section 1: Unique Index on Properties
+ */
+--Section 1 Setup
+SELECT create_vlabel('cypher_index', 'idx');
+CREATE UNIQUE INDEX cypher_index_idx_props_uq ON cypher_index.idx(properties);
+
+--Test 1
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+
+--Clean Up
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+
+--Test 2
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}), (:idx {i: 1}) $$) AS (a agtype);
+
+--Clean Up
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+
+--Test 3
+--Data Setup
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx) $$) AS (a agtype);
+
+--Query
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) SET n.i = 1$$) AS (a agtype);
+
+--Clean Up
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+
+--Test 4
+--create a vertex with i = 1
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+
+--delete the vertex
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+
+--we should be able to create a new vertex with the same value
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+
+--data cleanup
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+
+/*
+ * Test 5
+ *
+ * Same queries as Test 4, only in 1 transaction
+ */
+BEGIN TRANSACTION;
+--create a vertex with i = 1
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+
+--delete the vertex
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+
+--we should be able to create a new vertex with the same value
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+
+COMMIT;
+
+--data cleanup
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+
+
+--Test 6
+--create a vertex with i = 1
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+
+-- change the value
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) SET n.i = 2 $$) AS (a agtype);
+
+--we should be able to create a new vertex with the same value
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+
+--validate the data
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) RETURN n $$) AS (a agtype);
+
+--data cleanup
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+
+/*
+ * Test 7
+ *
+ * Same queries as Test 6, only in 1 transaction
+ */
+BEGIN TRANSACTION;
+--create a vertex with i = 1
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+
+-- change the value
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) SET n.i = 2 $$) AS (a agtype);
+
+--we should be able to create a new vertex with the same value
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+
+--validate the data
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) RETURN n $$) AS (a agtype);
+
+COMMIT;
+
+--validate the data again out of the transaction, just in case
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) RETURN n $$) AS (a agtype);
+
+--data cleanup
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+
+
+--Test 8
+--create a vertex with i = 1
+SELECT * FROM cypher('cypher_index', $$ CREATE (:idx {i: 1}) $$) AS (a agtype);
+
+-- Use Merge and force an index error
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) MERGE (n)-[:e]->(:idx {i: n.i}) $$) AS (a agtype);
+
+--data cleanup
+SELECT * FROM cypher('cypher_index', $$ MATCH(n) DETACH DELETE n $$) AS (a agtype);
+
+
+/*
+ * Section 2: Indices to Improve Query Runtime
+ */
+SELECT create_graph('agload_test_graph');
+
+ALTER TABLE agload_test_graph._ag_label_vertex
+CLUSTER ON _ag_label_vertex_pkey;
+
+SELECT create_vlabel('agload_test_graph','Country');
+SELECT load_labels_from_file('agload_test_graph', 'Country',
+    'age_load/countries.csv');
+
+ALTER TABLE agload_test_graph."Country" ADD PRIMARY KEY (id);
+CREATE UNIQUE INDEX CONCURRENTLY cntry_id_idx ON agload_test_graph."Country" (id);
+ALTER TABLE agload_test_graph."Country"  CLUSTER ON cntry_id_idx;
+
+
+SELECT create_vlabel('agload_test_graph','City');
+SELECT load_labels_from_file('agload_test_graph', 'City',
+    'age_load/cities.csv');
+
+ALTER TABLE agload_test_graph."City"
+ADD PRIMARY KEY (id);
+
+CREATE UNIQUE INDEX city_id_idx
+ON agload_test_graph."City" (id);
+
+ALTER TABLE agload_test_graph."City"
+CLUSTER ON city_id_idx;
+
+
+SELECT create_elabel('agload_test_graph','has_city');
+SELECT load_edges_from_file('agload_test_graph', 'has_city',
+     'age_load/edges.csv');
+
+ALTER TABLE agload_test_graph.has_city
+ADD CONSTRAINT has_city_end_fk FOREIGN KEY (end_id)
+REFERENCES agload_test_graph."Country"(id) MATCH FULL;
+
+CREATE INDEX load_has_city_eid_idx
+ON agload_test_graph.has_city (end_id);
+
+CREATE INDEX load_has_city_sid_idx
+ON agload_test_graph.has_city (start_id);
+
+ALTER TABLE agload_test_graph."has_city"
+CLUSTER ON load_has_city_eid_idx;
+
+SET enable_mergejoin = ON;
+SET enable_hashjoin = OFF;
+SET enable_nestloop = OFF;
+
+SELECT COUNT(*) FROM cypher('agload_test_graph', $$
+    MATCH (a:Country)<-[e:has_city]-()
+    RETURN e
+$$) as (n agtype);
+
+SET enable_mergejoin = OFF;
+SET enable_hashjoin = ON;
+SET enable_nestloop = OFF;
+
+SELECT COUNT(*) FROM cypher('agload_test_graph', $$
+    MATCH (a:Country)<-[e:has_city]-()
+    RETURN e
+$$) as (n agtype);
+
+SET enable_mergejoin = OFF;
+SET enable_hashjoin = OFF;
+SET enable_nestloop = ON;
+
+SELECT COUNT(*) FROM cypher('agload_test_graph', $$
+    MATCH (a:Country)<-[e:has_city]-()
+    RETURN e
+$$) as (n agtype);
+
+SELECT drop_graph('cypher_index', true);
+SELECT drop_graph('agload_test_graph', true);
diff --git a/src/backend/catalog/ag_label.c b/src/backend/catalog/ag_label.c
index 68b2c08..8001f53 100644
--- a/src/backend/catalog/ag_label.c
+++ b/src/backend/catalog/ag_label.c
@@ -276,7 +276,6 @@ List *get_all_edge_labels_per_graph(EState *estate, Oid graph_oid)
     HeapScanDesc scan_desc;
     HeapTuple tuple;
     TupleTableSlot *slot;
-    ResultRelInfo *resultRelInfo;
 
     // setup scan keys to get all edges for the given graph oid
     ScanKeyInit(&scan_keys[1], Anum_ag_label_graph, BTEqualStrategyNumber,
@@ -288,10 +287,8 @@ List *get_all_edge_labels_per_graph(EState *estate, Oid graph_oid)
     ag_label = heap_open(ag_label_relation_id(), RowExclusiveLock);
     scan_desc = heap_beginscan(ag_label, estate->es_snapshot, 2, scan_keys);
 
-    resultRelInfo = create_entity_result_rel_info(estate, "ag_catalog", "ag_label");
-
     slot = ExecInitExtraTupleSlot(estate,
-                RelationGetDescr(resultRelInfo->ri_RelationDesc));
+                RelationGetDescr(ag_label));
 
     // scan through the results and get all the label names.
     while(true)
@@ -316,7 +313,6 @@ List *get_all_edge_labels_per_graph(EState *estate, Oid graph_oid)
 
     heap_endscan(scan_desc);
     heap_close(ag_label, RowExclusiveLock);
-    heap_close(resultRelInfo->ri_RelationDesc, RowExclusiveLock);
 
     return labels;
 }
diff --git a/src/backend/executor/cypher_delete.c b/src/backend/executor/cypher_delete.c
index 3c76d5d..3d9406b 100644
--- a/src/backend/executor/cypher_delete.c
+++ b/src/backend/executor/cypher_delete.c
@@ -438,7 +438,7 @@ static void process_delete_list(CustomScanState *node)
         if (!HeapTupleIsValid(heap_tuple))
         {
             heap_endscan(scan_desc);
-            heap_close(resultRelInfo->ri_RelationDesc, RowExclusiveLock);
+            destroy_entity_result_rel_info(resultRelInfo);
 
             continue;
         }
@@ -460,7 +460,7 @@ static void process_delete_list(CustomScanState *node)
 
         /* Close the scan and the relation. */
         heap_endscan(scan_desc);
-        heap_close(resultRelInfo->ri_RelationDesc, RowExclusiveLock);
+        destroy_entity_result_rel_info(resultRelInfo);
     }
 }
 
@@ -541,7 +541,7 @@ static void find_connected_edges(CustomScanState *node, char *graph_name,
         }
 
         heap_endscan(scan_desc);
-        heap_close(resultRelInfo->ri_RelationDesc, RowExclusiveLock);
+        destroy_entity_result_rel_info(resultRelInfo);
     }
 
     Decrement_Estate_CommandId(estate);
diff --git a/src/backend/executor/cypher_set.c b/src/backend/executor/cypher_set.c
index 66cc86b..c9fba79 100644
--- a/src/backend/executor/cypher_set.c
+++ b/src/backend/executor/cypher_set.c
@@ -482,6 +482,7 @@ static void process_update_list(CustomScanState *node)
         }
 
         /* close relation */
+        ExecCloseIndices(resultRelInfo);
         heap_close(resultRelInfo->ri_RelationDesc, RowExclusiveLock);
 
         /* increment loop index */
diff --git a/src/backend/executor/cypher_utils.c b/src/backend/executor/cypher_utils.c
index ec086fd..8884ea1 100644
--- a/src/backend/executor/cypher_utils.c
+++ b/src/backend/executor/cypher_utils.c
@@ -46,7 +46,12 @@
 #include "utils/ag_cache.h"
 #include "utils/graphid.h"
 
-ResultRelInfo *create_entity_result_rel_info(EState *estate, char *graph_name, char *label_name)
+/*
+ * Given the graph name and the label name, create a ResultRelInfo for the table
+ * those to variables represent. Open the Indices too.
+ */
+ResultRelInfo *create_entity_result_rel_info(EState *estate, char *graph_name,
+                                             char *label_name)
 {
     RangeVar *rv;
     Relation label_relation;
@@ -67,15 +72,29 @@ ResultRelInfo *create_entity_result_rel_info(EState *estate, char *graph_name, c
 
     label_relation = parserOpenTable(pstate, rv, RowExclusiveLock);
 
+    // initialize the resultRelInfo
     InitResultRelInfo(resultRelInfo, label_relation,
                       list_length(estate->es_range_table), NULL,
                       estate->es_instrument);
 
+    // open the parse state
+    ExecOpenIndices(resultRelInfo, false);
+
     free_parsestate(pstate);
 
     return resultRelInfo;
 }
 
+// close the result_rel_info and close all the indices
+void destroy_entity_result_rel_info(ResultRelInfo *result_rel_info)
+{
+    // close the indices
+    ExecCloseIndices(result_rel_info);
+
+    // close the rel
+    heap_close(result_rel_info->ri_RelationDesc, RowExclusiveLock);
+}
+
 TupleTableSlot *populate_vertex_tts(
     TupleTableSlot *elemTupleSlot, agtype_value *id, agtype_value *properties)
 {
diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c
index 45f28ef..71fc41f 100644
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@ -196,7 +196,7 @@ static Node *make_vertex_expr(cypher_parsestate *cpstate, RangeTblEntry *rte,
                               char *label);
 static Node *make_edge_expr(cypher_parsestate *cpstate, RangeTblEntry *rte,
                             char *label);
-static FuncCall *make_qual(cypher_parsestate *cpstate,
+static Node *make_qual(cypher_parsestate *cpstate,
                            transform_entity *entity, char *name);
 static TargetEntry *
 transform_match_create_path_variable(cypher_parsestate *cpstate,
@@ -204,10 +204,10 @@ transform_match_create_path_variable(cypher_parsestate *cpstate,
 static List *make_path_join_quals(cypher_parsestate *cpstate, List *entities);
 static List *make_directed_edge_join_conditions(
     cypher_parsestate *cpstate, transform_entity *prev_entity,
-    transform_entity *next_entity, FuncCall *prev_qual, FuncCall *next_qual,
+    transform_entity *next_entity, Node *prev_qual, Node *next_qual,
     char *prev_node_label, char *next_node_label);
 static List *join_to_entity(cypher_parsestate *cpstate,
-                            transform_entity *entity, FuncCall *qual,
+                            transform_entity *entity, Node *qual,
                             enum transform_entity_join_side side);
 static List *make_join_condition_for_edge(cypher_parsestate *cpstate,
                                           transform_entity *prev_edge,
@@ -219,7 +219,7 @@ static List *make_edge_quals(cypher_parsestate *cpstate,
                              transform_entity *edge,
                              enum transform_entity_join_side side);
 static A_Expr *filter_vertices_on_label_id(cypher_parsestate *cpstate,
-                                           FuncCall *id_field, char *label);
+                                           Node *id_field, char *label);
 static transform_entity *
 make_transform_entity(cypher_parsestate *cpstate,
                       enum transform_entity_type type, Node *node, Expr *expr);
@@ -2670,7 +2670,7 @@ static FuncCall *prevent_duplicate_edges(cypher_parsestate *cpstate,
     foreach (lc, entities)
     {
         transform_entity *entity = lfirst(lc);
-        FuncCall *edge;
+        Node *edge;
 
         if (entity->type == ENT_EDGE)
         {
@@ -2695,7 +2695,7 @@ static FuncCall *prevent_duplicate_edges(cypher_parsestate *cpstate,
  */
 static List *make_directed_edge_join_conditions(
     cypher_parsestate *cpstate, transform_entity *prev_entity,
-    transform_entity *next_entity, FuncCall *prev_qual, FuncCall *next_qual,
+    transform_entity *next_entity, Node *prev_qual, Node *next_qual,
     char *prev_node_filter, char *next_node_filter)
 {
     List *quals = NIL;
@@ -2706,11 +2706,7 @@ static List *make_directed_edge_join_conditions(
                                                   prev_qual, JOIN_SIDE_LEFT));
     }
 
-    if (next_entity->in_join_tree && next_entity->type == ENT_VLE_EDGE)
-    {
-
-    }
-    else if (next_entity->in_join_tree)
+    if (next_entity->in_join_tree && next_entity->type != ENT_VLE_EDGE)
     {
         quals = list_concat(quals, join_to_entity(cpstate, next_entity,
                                                   next_qual, JOIN_SIDE_RIGHT));
@@ -2890,10 +2886,10 @@ static List *make_join_condition_for_edge(cypher_parsestate *cpstate,
     {
         case CYPHER_REL_DIR_RIGHT:
         {
-            FuncCall *prev_qual = make_qual(cpstate, entity,
-                                            AG_EDGE_COLNAME_START_ID);
-            FuncCall *next_qual = make_qual(cpstate, entity,
-                                            AG_EDGE_COLNAME_END_ID);
+            Node *prev_qual = make_qual(cpstate, entity,
+                                        AG_EDGE_COLNAME_START_ID);
+            Node *next_qual = make_qual(cpstate, entity,
+                                        AG_EDGE_COLNAME_END_ID);
 
             return make_directed_edge_join_conditions(cpstate, prev_entity,
                                                       next_node, prev_qual,
@@ -2903,10 +2899,10 @@ static List *make_join_condition_for_edge(cypher_parsestate *cpstate,
         }
         case CYPHER_REL_DIR_LEFT:
         {
-            FuncCall *prev_qual = make_qual(cpstate, entity,
-                                            AG_EDGE_COLNAME_END_ID);
-            FuncCall *next_qual = make_qual(cpstate, entity,
-                                            AG_EDGE_COLNAME_START_ID);
+            Node *prev_qual = make_qual(cpstate, entity,
+                                        AG_EDGE_COLNAME_END_ID);
+            Node *next_qual = make_qual(cpstate, entity,
+                                        AG_EDGE_COLNAME_START_ID);
 
             return make_directed_edge_join_conditions(cpstate, prev_entity,
                                                       next_node, prev_qual,
@@ -2920,10 +2916,10 @@ static List *make_join_condition_for_edge(cypher_parsestate *cpstate,
              * For undirected relationships, we can use the left directed
              * relationship OR'd by the right directed relationship.
              */
-            FuncCall *start_id_expr = make_qual(cpstate, entity,
-                                                AG_EDGE_COLNAME_START_ID);
-            FuncCall *end_id_expr = make_qual(cpstate, entity,
-                                              AG_EDGE_COLNAME_END_ID);
+            Node *start_id_expr = make_qual(cpstate, entity,
+                                            AG_EDGE_COLNAME_START_ID);
+            Node *end_id_expr = make_qual(cpstate, entity,
+                                          AG_EDGE_COLNAME_END_ID);
             List *first_join_quals = NIL, *second_join_quals = NIL;
             Expr *first_qual, *second_qual;
             Expr *or_qual;
@@ -2994,7 +2990,7 @@ static Node *make_bool_a_const(bool state)
  * passed entity is a directed edge.
  */
 static List *join_to_entity(cypher_parsestate *cpstate,
-                            transform_entity *entity, FuncCall *qual,
+                            transform_entity *entity, Node *qual,
                             enum transform_entity_join_side side)
 {
     ParseState *pstate = (ParseState *)cpstate;
@@ -3003,10 +2999,9 @@ static List *join_to_entity(cypher_parsestate *cpstate,
 
     if (entity->type == ENT_VERTEX)
     {
-        FuncCall *id_qual = make_qual(cpstate, entity, AG_EDGE_COLNAME_ID);
+        Node *id_qual = make_qual(cpstate, entity, AG_EDGE_COLNAME_ID);
 
-        expr = makeSimpleA_Expr(AEXPR_OP, "=", (Node *)qual, (Node *)id_qual,
-                                -1);
+        expr = makeSimpleA_Expr(AEXPR_OP, "=", qual, (Node *)id_qual, -1);
 
         quals = lappend(quals, expr);
     }
@@ -3016,12 +3011,12 @@ static List *join_to_entity(cypher_parsestate *cpstate,
 
         if (list_length(edge_quals) > 1)
         {
-            expr = makeSimpleA_Expr(AEXPR_IN, "=", (Node *)qual,
+            expr = makeSimpleA_Expr(AEXPR_IN, "=", qual,
                                     (Node *)edge_quals, -1);
         }
         else
         {
-            expr = makeSimpleA_Expr(AEXPR_OP, "=", (Node *)qual,
+            expr = makeSimpleA_Expr(AEXPR_OP, "=", qual,
                                     linitial(edge_quals), -1);
         }
 
@@ -3147,13 +3142,13 @@ static List *make_edge_quals(cypher_parsestate *cpstate,
  * that removes all labels that do not have the same label_id
  */
 static A_Expr *filter_vertices_on_label_id(cypher_parsestate *cpstate,
-                                           FuncCall *id_field, char *label)
+                                           Node *id_field, char *label)
 {
     label_cache_data *lcd = search_label_name_graph_cache(label,
                                                           cpstate->graph_oid);
     A_Const *n;
-    FuncCall *fc, *conversion_fc;
-    Value *ag_catalog, *extract_label_id, *agtype_to_graphid;
+    FuncCall *fc;
+    Value *ag_catalog, *extract_label_id;
     int32 label_id = lcd->id;
 
     n = makeNode(A_Const);
@@ -3163,13 +3158,9 @@ 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("agtype_to_graphid");
-
-    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(conversion_fc), -1);
+                      list_make1(id_field), -1);
 
     return makeSimpleA_Expr(AEXPR_OP, "=", (Node *)fc, (Node *)n, -1);
 }
@@ -3775,10 +3766,11 @@ static char *get_accessor_function_name(enum transform_entity_type type,
  * For the given entity and column name, construct an expression that will
  * access the column or get the access function if the entity is a variable.
  */
-static FuncCall *make_qual(cypher_parsestate *cpstate,
-                           transform_entity *entity, char *col_name)
+static Node *make_qual(cypher_parsestate *cpstate,
+                       transform_entity *entity, char *col_name)
 {
     List *qualified_name, *args;
+    Node *node;
 
     if (IsA(entity->expr, Var))
     {
@@ -3789,7 +3781,9 @@ static FuncCall *make_qual(cypher_parsestate *cpstate,
         qualified_name = list_make2(makeString("ag_catalog"),
                                     makeString(function_name));
 
+
         args = list_make1(entity->expr);
+        node = (Node *)makeFuncCall(qualified_name, args, -1);
     }
     else
     {
@@ -3801,19 +3795,25 @@ static FuncCall *make_qual(cypher_parsestate *cpstate,
                                     makeString("graphid_to_agtype"));
 
         if (entity->type == ENT_EDGE)
+        {
             entity_name = entity->entity.node->name;
+        }
         else if (entity->type == ENT_VERTEX)
+        {
             entity_name = entity->entity.rel->name;
+        }
         else
+        {
             ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                             errmsg("unknown entity type")));
+        }
 
         cr->fields = list_make2(makeString(entity_name), makeString(col_name));
 
-        args = list_make1(cr);
+        node = (Node *)cr;
     }
 
-    return makeFuncCall(qualified_name, args, -1);
+    return node;
 }
 
 static Expr *transform_cypher_edge(cypher_parsestate *cpstate,
diff --git a/src/backend/parser/cypher_expr.c b/src/backend/parser/cypher_expr.c
index 81c9b64..2af3db7 100644
--- a/src/backend/parser/cypher_expr.c
+++ b/src/backend/parser/cypher_expr.c
@@ -1186,7 +1186,6 @@ static Node *transform_SubLink(cypher_parsestate *cpstate, SubLink *sublink)
                  parser_errposition(pstate, sublink->location)));
 
     pstate->p_hasSubLinks = true;
-
     /*
      * OK, let's transform the sub-SELECT.
      */
diff --git a/src/backend/utils/adt/age_vle.c b/src/backend/utils/adt/age_vle.c
index 1b5b3f3..5d316cc 100644
--- a/src/backend/utils/adt/age_vle.c
+++ b/src/backend/utils/adt/age_vle.c
@@ -1908,16 +1908,38 @@ PG_FUNCTION_INFO_V1(age_match_vle_edge_to_id_qual);
 
 Datum age_match_vle_edge_to_id_qual(PG_FUNCTION_ARGS)
 {
+    int nargs = 0;
+    Datum *args = NULL;
+    bool *nulls = NULL;
+    Oid *types = NULL;
     agtype *agt_arg_vpc = NULL;
     agtype *edge_id = NULL;
     agtype *pos_agt = NULL;
     agtype_value *id, *position;
     VLE_path_container *vle_path = NULL;
-    graphid *array;
-    bool vle_is_on_left;
+    graphid *array = NULL;
+    bool vle_is_on_left = false;
+    graphid gid = 0;
+
+    /* extract argument values */
+    nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
+
+    if (nargs != 3)
+    {
+        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                        errmsg("age_match_vle_edge_to_id_qual() invalid number of arguments")));
+    }
+
+    /* the arguments cannot be NULL */
+    if (nulls[0] || nulls[1] || nulls[2])
+    {
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("age_match_vle_edge_to_id_qual() arguments must be non NULL")));
+    }
 
     /* get the VLE_path_container argument */
-    agt_arg_vpc = AG_GET_ARG_AGTYPE_P(0);
+    agt_arg_vpc = DATUM_GET_AGTYPE_P(args[0]);
 
     if (!AGT_ROOT_IS_BINARY(agt_arg_vpc) ||
         AGT_ROOT_BINARY_FLAGS(agt_arg_vpc) != AGT_FBINARY_TYPE_VLE_PATH)
@@ -1931,23 +1953,40 @@ Datum age_match_vle_edge_to_id_qual(PG_FUNCTION_ARGS)
     vle_path = (VLE_path_container *)agt_arg_vpc;
     array = GET_GRAPHID_ARRAY_FROM_CONTAINER(vle_path);
 
-    /* Get the edge id we are checking the end of the list too */
-    edge_id = AG_GET_ARG_AGTYPE_P(1);
-
-    if (!AGT_ROOT_IS_SCALAR(edge_id))
+    if (types[1] == AGTYPEOID)
     {
-        ereport(ERROR,
-                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                 errmsg("argument 2 of age_match_vle_edge_to_edge_qual must be an integer")));
+        /* Get the edge id we are checking the end of the list too */
+        edge_id = AG_GET_ARG_AGTYPE_P(1);
+
+        if (!AGT_ROOT_IS_SCALAR(edge_id))
+        {
+            ereport(ERROR,
+                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                     errmsg("argument 2 of age_match_vle_edge_to_edge_qual must be an integer")));
+        }
+
+        id = get_ith_agtype_value_from_container(&edge_id->root, 0);
+
+        if (id->type != AGTV_INTEGER)
+        {
+            ereport(ERROR,
+                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                     errmsg("argument 2 of age_match_vle_edge_to_edge_qual must be an integer")));
+        }
+
+        gid = id->val.int_value;
     }
+    else if (types[1] == GRAPHIDOID)
+    {
 
-    id = get_ith_agtype_value_from_container(&edge_id->root, 0);
+        gid = DATUM_GET_GRAPHID(args[1]);
 
-    if (id->type != AGTV_INTEGER)
+    }
+    else
     {
         ereport(ERROR,
                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                 errmsg("argument 2 of age_match_vle_edge_to_edge_qual must be an integer")));
+                 errmsg("match_vle_terminal_edge() arguement 1 must be an agtype integer or a graphid")));
     }
 
     pos_agt = AG_GET_ARG_AGTYPE_P(2);
@@ -1980,7 +2019,7 @@ Datum age_match_vle_edge_to_id_qual(PG_FUNCTION_ARGS)
          * that was passed in the second arg. The transform logic is responsible
          * for making that the start or end id, depending on its direction.
          */
-        if (id->val.int_value != array[array_size - 1])
+        if (gid != array[array_size - 1])
         {
             PG_RETURN_BOOL(false);
         }
@@ -1993,7 +2032,7 @@ Datum age_match_vle_edge_to_id_qual(PG_FUNCTION_ARGS)
          * Path is like ...[edge]-()-[vle_edge]... Get the vertex at the start
          * of the vle edge and check against id.
          */
-       if (id->val.int_value != array[0])
+        if (gid != array[0])
         {
             PG_RETURN_BOOL(false);
         }
@@ -2097,6 +2136,10 @@ PG_FUNCTION_INFO_V1(age_match_vle_terminal_edge);
 
 Datum age_match_vle_terminal_edge(PG_FUNCTION_ARGS)
 {
+    int nargs = 0;
+    Datum *args = NULL;
+    bool *nulls = NULL;
+    Oid *types = NULL;
     VLE_path_container *vpc = NULL;
     agtype *agt_arg_vsid = NULL;
     agtype *agt_arg_veid = NULL;
@@ -2104,37 +2147,35 @@ Datum age_match_vle_terminal_edge(PG_FUNCTION_ARGS)
     agtype_value *agtv_temp = NULL;
     graphid vsid = 0;
     graphid veid = 0;
-    bool has_vsid = false;
-    bool has_veid = false;
     graphid *gida = NULL;
     int gidasize = 0;
 
-    /* the VLE_path_container argument cannot be NULL */
-    if (PG_ARGISNULL(2))
+    /* extract argument values */
+    nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
+
+    if (nargs != 3)
     {
-        ereport(ERROR,
-                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                 errmsg("match_vle_terminal_edge() argument 3 must be non NULL")));
+        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                        errmsg("age_match_terminal_edge() invalid number of arguments")));
     }
 
-    /* one or both vsid and veid needs to be non NULL */
-    if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
+    /* the arguments cannot be NULL */
+    if (nulls[0] || nulls[1] || nulls[2])
     {
         ereport(ERROR,
                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                 errmsg("match_vle_terminal_edge() argument 1 or 2 must be non NULL")));
-
+                 errmsg("match_vle_terminal_edge() arguments cannot be NULL")));
     }
 
     /* get the vpc */
-    agt_arg_path = AG_GET_ARG_AGTYPE_P(2);
+    agt_arg_path = DATUM_GET_AGTYPE_P(args[2]);
 
     /* it cannot be NULL */
     if (is_agtype_null(agt_arg_path))
     {
         ereport(ERROR,
                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                 errmsg("match_vle_terminal_edge() argument 3 must be non NULL")));
+                 errmsg("match_vle_terminal_edge() argument 3 cannot be NULL")));
     }
 
     /*
@@ -2142,7 +2183,7 @@ Datum age_match_vle_terminal_edge(PG_FUNCTION_ARGS)
      * the container must be an AGT_FBINARY_TYPE_VLE_PATH.
      */
     Assert(AGT_ROOT_IS_BINARY(agt_arg_path));
-    Assert(AGT_ROOT_BINARY_FLAGS(agt_arg_path) == AGT_FBINARY_TYPE_VLE_PATH );
+    Assert(AGT_ROOT_BINARY_FLAGS(agt_arg_path) == AGT_FBINARY_TYPE_VLE_PATH);
 
     /* get the container */
     vpc = (VLE_path_container *)agt_arg_path;
@@ -2157,44 +2198,69 @@ Datum age_match_vle_terminal_edge(PG_FUNCTION_ARGS)
     Assert(gidasize >= 3 || gidasize == 1);
 
     /* get the vsid */
-    if (!PG_ARGISNULL(0))
+    if (types[0] == AGTYPEOID)
     {
-        agt_arg_vsid = AG_GET_ARG_AGTYPE_P(0);
+        agt_arg_vsid = DATUM_GET_AGTYPE_P(args[0]);
 
         if (!is_agtype_null(agt_arg_vsid))
         {
-            agtv_temp = get_ith_agtype_value_from_container(&agt_arg_vsid->root,
-                                                            0);
+
+            agtv_temp =
+               get_ith_agtype_value_from_container(&agt_arg_vsid->root, 0);
+
             Assert(agtv_temp->type == AGTV_INTEGER);
             vsid = agtv_temp->val.int_value;
-            has_vsid = true;
         }
+        else
+        {
+            ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("match_vle_terminal_edge() argument 1 must be non NULL")));
+        }
+    }
+    else if (types[0] == GRAPHIDOID)
+    {
+        vsid = DATUM_GET_GRAPHID(args[0]);
+    }
+    else
+    {
+        ereport(ERROR,
+            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+             errmsg("match_vle_terminal_edge() arguement 1 must be an agtype integer or a graphid")));
     }
 
     /* get the veid */
-    if (!PG_ARGISNULL(1))
+    if (types[1] == AGTYPEOID)
     {
-        agt_arg_veid = AG_GET_ARG_AGTYPE_P(1);
+        agt_arg_veid = DATUM_GET_AGTYPE_P(args[1]);
+
         if (!is_agtype_null(agt_arg_veid))
         {
             agtv_temp = get_ith_agtype_value_from_container(&agt_arg_veid->root,
                                                             0);
             Assert(agtv_temp->type == AGTV_INTEGER);
             veid = agtv_temp->val.int_value;
-            has_veid = true;
         }
-    }
-
-    if (!(has_vsid || has_veid))
-    {
+        else
+        {
             ereport(ERROR,
                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                 errmsg("match_vle_terminal_edge() argument 1 or 2 must be non NULL")));
+                 errmsg("match_vle_terminal_edge() argument 2 must be non NULL")));
+        }
+    }
+    else if (types[1] == GRAPHIDOID)
+    {
+        veid = DATUM_GET_GRAPHID(args[1]);
+    }
+    else
+    {
+        ereport(ERROR,
+            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+             errmsg("match_vle_terminal_edge() arguement 2 must be an agtype integer or a graphid")));
     }
 
     /* compare the path beginning or end points */
-    PG_RETURN_BOOL((has_vsid ? gida[0] == vsid : true) &&
-                   (has_veid ? veid == gida[gidasize - 1] : true));
+    PG_RETURN_BOOL(gida[0] == vsid && veid == gida[gidasize - 1]);
 }
 
 /* PG helper function to build an agtype (Datum) edge for matching */
@@ -2338,41 +2404,104 @@ Datum _ag_enforce_edge_uniqueness(PG_FUNCTION_ARGS)
     /* insert arguments into hash table */
     for (i = 0; i < nargs; i++)
     {
-        agtype *agt_i = NULL;
+        /* if it is an INT8OID or a GRAPHIDOID */
+        if (types[i] == INT8OID || types[i] == GRAPHIDOID)
+        {
+            graphid edge_id = 0;
+            bool found = false;
+            int64 *value = NULL;
 
-        /* get the argument */
-        agt_i = DATUM_GET_AGTYPE_P(args[i]);
+            edge_id = DatumGetInt64(args[i]);
+
+            /* insert the edge_id */
+            value = (int64 *)hash_search(exists_hash, (void *)&edge_id,
+                                         HASH_ENTER, &found);
+
+            /* if we found it, we're done, we have a duplicate */
+            if (found)
+            {
+                hash_destroy(exists_hash);
+                PG_RETURN_BOOL(false);
+            }
+            /* otherwise, add it to the returned bucket */
+            else
+            {
+                *value = edge_id;
+            }
 
-        /* if the argument is an AGTYPE VLE_path_container */
-        if (types[i] == AGTYPEOID &&
-            AGT_ROOT_IS_BINARY(agt_i) &&
-            AGT_ROOT_BINARY_FLAGS(agt_i) == AGT_FBINARY_TYPE_VLE_PATH)
+            continue;
+        }
+        else if (types[i] == AGTYPEOID)
         {
-            VLE_path_container *vpc = NULL;
-            graphid *graphid_array = NULL;
-            int64 graphid_array_size = 0;
-            int64 j = 0;
+            /* get the argument */
+            agtype *agt_i = DATUM_GET_AGTYPE_P(args[i]);
 
-            /* cast to VLE_path_container */
-            vpc = (VLE_path_container *)agt_i;
+            /* if the argument is an AGTYPE VLE_path_container */
+            if (AGT_ROOT_IS_BINARY(agt_i) &&
+                AGT_ROOT_BINARY_FLAGS(agt_i) == AGT_FBINARY_TYPE_VLE_PATH)
+            {
+                VLE_path_container *vpc = NULL;
+                graphid *graphid_array = NULL;
+                int64 graphid_array_size = 0;
+                int64 j = 0;
 
-            /* get the graphid array */
-            graphid_array = GET_GRAPHID_ARRAY_FROM_CONTAINER(vpc);
+                /* cast to VLE_path_container */
+                vpc = (VLE_path_container *)agt_i;
 
-            /* get the graphid array size */
-            graphid_array_size = vpc->graphid_array_size;
+                /* get the graphid array */
+                graphid_array = GET_GRAPHID_ARRAY_FROM_CONTAINER(vpc);
 
-            /* insert all the edges in the vpc, into the hash table */
-            for (j = 1; j < graphid_array_size - 1; j+=2)
+                /* get the graphid array size */
+                graphid_array_size = vpc->graphid_array_size;
+
+                /* insert all the edges in the vpc, into the hash table */
+                for (j = 1; j < graphid_array_size - 1; j+=2)
+                {
+                    int64 *value = NULL;
+                    bool found = false;
+                    graphid edge_id = 0;
+
+                    /* get the edge id */
+                    edge_id = graphid_array[j];
+
+                    /* insert the edge id */
+                    value = (int64 *)hash_search(exists_hash, (void *)&edge_id,
+                                                 HASH_ENTER, &found);
+
+                    /* if we found it, we're done, we have a duplicate */
+                    if (found)
+                    {
+                        hash_destroy(exists_hash);
+                        PG_RETURN_BOOL(false);
+                    }
+                    /* otherwise, add it to the returned bucket */
+                    else
+                    {
+                        *value = edge_id;
+                    }
+                }
+            }
+            /* if it is a regular AGTYPE scalar */
+            else if (AGT_ROOT_IS_SCALAR(agt_i))
             {
+                agtype_value *agtv_id = NULL;
                 int64 *value = NULL;
                 bool found = false;
                 graphid edge_id = 0;
 
-                /* get the edge id */
-                edge_id = graphid_array[j];
+                agtv_id = get_ith_agtype_value_from_container(&agt_i->root, 0);
+
+                if (agtv_id->type != AGTV_INTEGER)
+                {
+                    ereport(ERROR,
+                            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                             errmsg("_ag_enforce_edge_uniqueness parameter %d must resolve to an agtype integer",
+                                    i)));
+                }
+
+                edge_id = agtv_id->val.int_value;
 
-                /* insert the edge id */
+                /* insert the edge_id */
                 value = (int64 *)hash_search(exists_hash, (void *)&edge_id,
                                              HASH_ENTER, &found);
 
@@ -2388,69 +2517,13 @@ Datum _ag_enforce_edge_uniqueness(PG_FUNCTION_ARGS)
                     *value = edge_id;
                 }
             }
-        }
-        /* if it is a regular AGTYPE scalar */
-        else if (types[i] == AGTYPEOID &&
-                 AGT_ROOT_IS_SCALAR(agt_i))
-        {
-            agtype_value *agtv_id = NULL;
-            int64 *value = NULL;
-            bool found = false;
-            graphid edge_id = 0;
-
-            agtv_id = get_ith_agtype_value_from_container(&agt_i->root, 0);
-
-            if (agtv_id->type != AGTV_INTEGER)
+            else
             {
                 ereport(ERROR,
                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                         errmsg("_ag_enforce_edge_uniqueness parameter %d must resolve to an agtype integer",
+                         errmsg("_ag_enforce_edge_uniqueness invalid parameter type %d",
                                 i)));
             }
-
-            edge_id = agtv_id->val.int_value;
-
-            /* insert the edge_id */
-            value = (int64 *)hash_search(exists_hash, (void *)&edge_id,
-                                         HASH_ENTER, &found);
-
-            /* if we found it, we're done, we have a duplicate */
-            if (found)
-            {
-                hash_destroy(exists_hash);
-                PG_RETURN_BOOL(false);
-            }
-            /* otherwise, add it to the returned bucket */
-            else
-            {
-                *value = edge_id;
-            }
-        }
-        /* if it is an INT8OID or a GRAPHIDOID */
-        else if (types[i] == INT8OID ||
-                 types[i] == GRAPHIDOID)
-        {
-            graphid edge_id = 0;
-            bool found = false;
-            int64 *value = NULL;
-
-            edge_id = DatumGetInt64(args[i]);
-
-            /* insert the edge_id */
-            value = (int64 *)hash_search(exists_hash, (void *)&edge_id,
-                                         HASH_ENTER, &found);
-
-            /* if we found it, we're done, we have a duplicate */
-            if (found)
-            {
-                hash_destroy(exists_hash);
-                PG_RETURN_BOOL(false);
-            }
-            /* otherwise, add it to the returned bucket */
-            else
-            {
-                *value = edge_id;
-            }
         }
         /* it is neither a VLE_path_container, AGTYPE, INT8, or a GRAPHIDOID */
         else
diff --git a/src/backend/utils/adt/graphid.c b/src/backend/utils/adt/graphid.c
index 2b0054e..131e6c3 100644
--- a/src/backend/utils/adt/graphid.c
+++ b/src/backend/utils/adt/graphid.c
@@ -215,3 +215,14 @@ Datum _graphid(PG_FUNCTION_ARGS)
 
     AG_RETURN_GRAPHID(gid);
 }
+
+//Hashing Function for Hash Indexes
+PG_FUNCTION_INFO_V1(graphid_hash_cmp);
+
+Datum graphid_hash_cmp(PG_FUNCTION_ARGS)
+{
+    graphid l = AG_GETARG_GRAPHID(0);
+    int hash = (int) ((l >> 32) ^ l);// ^ seed;
+
+    PG_RETURN_INT32(hash);
+}
diff --git a/src/include/executor/cypher_utils.h b/src/include/executor/cypher_utils.h
index b2102d4..55c3c1e 100644
--- a/src/include/executor/cypher_utils.h
+++ b/src/include/executor/cypher_utils.h
@@ -89,12 +89,15 @@ typedef struct cypher_merge_custom_scan_state
     bool found_a_path;
 } cypher_merge_custom_scan_state;
 
-TupleTableSlot *populate_vertex_tts(TupleTableSlot *elemTupleSlot, agtype_value *id, agtype_value *properties);
+TupleTableSlot *populate_vertex_tts(TupleTableSlot *elemTupleSlot,
+                                    agtype_value *id, agtype_value *properties);
 TupleTableSlot *populate_edge_tts(
     TupleTableSlot *elemTupleSlot, agtype_value *id, agtype_value *startid,
     agtype_value *endid, agtype_value *properties);
 
-ResultRelInfo *create_entity_result_rel_info(EState *estate, char *graph_name, char *label_name);
+ResultRelInfo *create_entity_result_rel_info(EState *estate, char *graph_name,
+                                             char *label_name);
+void destroy_entity_result_rel_info(ResultRelInfo *result_rel_info);
 
 bool entity_exists(EState *estate, Oid graph_oid, graphid id);
 HeapTuple insert_entity_tuple(ResultRelInfo *resultRelInfo,