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 2021/06/24 20:29:23 UTC

[incubator-age] branch master updated: Refactor HeapTuple Retrieval Logic - Corrected

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 d3282d6  Refactor HeapTuple Retrieval Logic - Corrected
d3282d6 is described below

commit d3282d6814b81c1663b152fb8dd537fe8bb381eb
Author: Josh Innis <Jo...@gmail.com>
AuthorDate: Thu Jun 24 13:24:36 2021 -0700

    Refactor HeapTuple Retrieval Logic - Corrected
    
    Resolves #55 and #89
    
    The logic in SET and DELETE to get the most recent tuple to update
    was rewritten. Rather than scan the execution tree to find the heap
    tuple, it uses the id for the vertex/edge to find the most recent
    heap tuple on disc.
    
    Allow DELETE to be the final clause in a cypher query.
---
 regress/expected/cypher_delete.out   |  85 ++++++++++-
 regress/sql/cypher_delete.sql        |  28 +++-
 src/backend/executor/cypher_create.c |  41 +-----
 src/backend/executor/cypher_delete.c |  94 +++++++-----
 src/backend/executor/cypher_set.c    |  88 ++++++------
 src/backend/executor/cypher_utils.c  | 270 ++---------------------------------
 src/backend/parser/cypher_analyze.c  |   3 +-
 src/include/executor/cypher_utils.h  |  19 +--
 8 files changed, 230 insertions(+), 398 deletions(-)

diff --git a/regress/expected/cypher_delete.out b/regress/expected/cypher_delete.out
index 0b0980b..cc268c5 100644
--- a/regress/expected/cypher_delete.out
+++ b/regress/expected/cypher_delete.out
@@ -18,8 +18,6 @@
  */
 LOAD 'age';
 SET search_path TO ag_catalog;
-SELECT drop_graph('cypher_delete', true);
-ERROR:  graph "cypher_delete" does not exist
 SELECT create_graph('cypher_delete');
 NOTICE:  graph "cypher_delete" has been created
  create_graph 
@@ -496,16 +494,97 @@ SELECT delete_test();
  {"id": 844424930132011, "label": "v", "properties": {}}::vertex
 (1 row)
 
+-- Clean Up
+SELECT * FROM cypher('cypher_delete', $$MATCH(n) DETACH DELETE n RETURN n$$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+--Test 23
+SELECT * FROM cypher('cypher_delete', $$CREATE (n:v)-[:e]->()$$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_delete', $$CREATE (n:v)-[:e2]->()$$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_delete', $$MATCH p=()-[]->() RETURN p$$) AS (a agtype);
+                                                                                                                                a                                                                                                                                 
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 844424930132013, "label": "v", "properties": {}}::vertex, {"id": 2251799813685249, "label": "e2", "end_id": 281474976710658, "start_id": 844424930132013, "properties": {}}::edge, {"id": 281474976710658, "label": "", "properties": {}}::vertex]::path
+ [{"id": 844424930132012, "label": "v", "properties": {}}::vertex, {"id": 1125899906842643, "label": "e", "end_id": 281474976710657, "start_id": 844424930132012, "properties": {}}::edge, {"id": 281474976710657, "label": "", "properties": {}}::vertex]::path
+(2 rows)
+
+SELECT * FROM cypher('cypher_delete', $$MATCH(n)-[e]->(m)  DELETE e$$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_delete', $$MATCH p=()-[]->() RETURN p$$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+-- Clean Up
+SELECT * FROM cypher('cypher_delete', $$MATCH(n)  DELETE n RETURN n$$) AS (a agtype);
+                                a                                
+-----------------------------------------------------------------
+ {"id": 281474976710657, "label": "", "properties": {}}::vertex
+ {"id": 281474976710658, "label": "", "properties": {}}::vertex
+ {"id": 844424930132012, "label": "v", "properties": {}}::vertex
+ {"id": 844424930132013, "label": "v", "properties": {}}::vertex
+(4 rows)
+
+--Test 24
+SELECT * FROM cypher('cypher_delete', $$CREATE (n:v)-[:e]->()$$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_delete', $$CREATE (n:v)-[:e2]->()$$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_delete', $$MATCH p=()-[]->() RETURN p$$) AS (a agtype);
+                                                                                                                                a                                                                                                                                 
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 844424930132015, "label": "v", "properties": {}}::vertex, {"id": 2251799813685250, "label": "e2", "end_id": 281474976710660, "start_id": 844424930132015, "properties": {}}::edge, {"id": 281474976710660, "label": "", "properties": {}}::vertex]::path
+ [{"id": 844424930132014, "label": "v", "properties": {}}::vertex, {"id": 1125899906842644, "label": "e", "end_id": 281474976710659, "start_id": 844424930132014, "properties": {}}::edge, {"id": 281474976710659, "label": "", "properties": {}}::vertex]::path
+(2 rows)
+
+SELECT * FROM cypher('cypher_delete', $$MATCH(n)-[]->() DETACH DELETE n$$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_delete', $$MATCH p=()-[]->() RETURN p$$) AS (a agtype);
+ a 
+---
+(0 rows)
+
+-- Clean Up
+SELECT * FROM cypher('cypher_delete', $$MATCH(n) DELETE n RETURN n$$) AS (a agtype);
+                               a                                
+----------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {}}::vertex
+ {"id": 281474976710660, "label": "", "properties": {}}::vertex
+(2 rows)
+
 --
 -- Clean up
 --
 DROP FUNCTION delete_test;
 SELECT drop_graph('cypher_delete', true);
-NOTICE:  drop cascades to 4 other objects
+NOTICE:  drop cascades to 5 other objects
 DETAIL:  drop cascades to table cypher_delete._ag_label_vertex
 drop cascades to table cypher_delete._ag_label_edge
 drop cascades to table cypher_delete.v
 drop cascades to table cypher_delete.e
+drop cascades to table cypher_delete.e2
 NOTICE:  graph "cypher_delete" has been dropped
  drop_graph 
 ------------
diff --git a/regress/sql/cypher_delete.sql b/regress/sql/cypher_delete.sql
index f062722..2937a23 100644
--- a/regress/sql/cypher_delete.sql
+++ b/regress/sql/cypher_delete.sql
@@ -19,7 +19,7 @@
 
 LOAD 'age';
 SET search_path TO ag_catalog;
-SELECT drop_graph('cypher_delete', true);
+
 SELECT create_graph('cypher_delete');
 
 --Test 1: Delete Vertices
@@ -196,6 +196,32 @@ SELECT delete_test();
 
 SELECT * FROM cypher('cypher_delete', $$CREATE (v:v)$$) AS (a agtype);
 SELECT delete_test();
+
+-- Clean Up
+SELECT * FROM cypher('cypher_delete', $$MATCH(n) DETACH DELETE n RETURN n$$) AS (a agtype);
+
+--Test 23
+SELECT * FROM cypher('cypher_delete', $$CREATE (n:v)-[:e]->()$$) AS (a agtype);
+SELECT * FROM cypher('cypher_delete', $$CREATE (n:v)-[:e2]->()$$) AS (a agtype);
+
+SELECT * FROM cypher('cypher_delete', $$MATCH p=()-[]->() RETURN p$$) AS (a agtype);
+SELECT * FROM cypher('cypher_delete', $$MATCH(n)-[e]->(m)  DELETE e$$) AS (a agtype);
+SELECT * FROM cypher('cypher_delete', $$MATCH p=()-[]->() RETURN p$$) AS (a agtype);
+
+-- Clean Up
+SELECT * FROM cypher('cypher_delete', $$MATCH(n)  DELETE n RETURN n$$) AS (a agtype);
+
+--Test 24
+SELECT * FROM cypher('cypher_delete', $$CREATE (n:v)-[:e]->()$$) AS (a agtype);
+SELECT * FROM cypher('cypher_delete', $$CREATE (n:v)-[:e2]->()$$) AS (a agtype);
+
+SELECT * FROM cypher('cypher_delete', $$MATCH p=()-[]->() RETURN p$$) AS (a agtype);
+SELECT * FROM cypher('cypher_delete', $$MATCH(n)-[]->() DETACH DELETE n$$) AS (a agtype);
+SELECT * FROM cypher('cypher_delete', $$MATCH p=()-[]->() RETURN p$$) AS (a agtype);
+
+-- Clean Up
+SELECT * FROM cypher('cypher_delete', $$MATCH(n) DELETE n RETURN n$$) AS (a agtype);
+
 --
 -- Clean up
 --
diff --git a/src/backend/executor/cypher_create.c b/src/backend/executor/cypher_create.c
index c7aeb30..edf2d0d 100644
--- a/src/backend/executor/cypher_create.c
+++ b/src/backend/executor/cypher_create.c
@@ -154,8 +154,6 @@ static void process_pattern(cypher_create_custom_scan_state *css)
 {
     ListCell *lc2;
 
-    css->tuple_info = NIL;
-
     foreach (lc2, css->pattern)
     {
         cypher_create_path *path = lfirst(lc2);
@@ -222,8 +220,6 @@ static TupleTableSlot *exec_cypher_create(CustomScanState *node)
             econtext->ecxt_scantuple =
                 node->ss.ps.lefttree->ps_ProjInfo->pi_exprContext->ecxt_scantuple;
 
-            css->tuple_info = NIL;
-
             process_pattern(css);
         }
 
@@ -243,8 +239,6 @@ static TupleTableSlot *exec_cypher_create(CustomScanState *node)
         econtext->ecxt_scantuple =
             node->ss.ps.lefttree->ps_ProjInfo->pi_exprContext->ecxt_scantuple;
 
-        css->tuple_info = NIL;
-
         process_pattern(css);
 
         econtext->ecxt_scantuple =
@@ -310,7 +304,6 @@ Node *create_cypher_create_plan_state(CustomScan *cscan)
 
     cypher_css->path_values = NIL;
     cypher_css->pattern = target_nodes->paths;
-    cypher_css->tuple_info = NIL;
     cypher_css->flags = target_nodes->flags;
     cypher_css->graph_oid = target_nodes->graph_oid;
 
@@ -336,7 +329,6 @@ static void create_edge(cypher_create_custom_scan_state *css,
     Datum id;
     Datum start_id, end_id, next_vertex_id;
     List *prev_path = css->path_values;
-    HeapTuple tuple;
 
     Assert(node->type == LABEL_KIND_EDGE);
     Assert(lfirst(next) != NULL);
@@ -400,10 +392,7 @@ static void create_edge(cypher_create_custom_scan_state *css,
         scanTupleSlot->tts_isnull[node->prop_attr_num];
 
     // Insert the new edge
-    tuple = insert_entity_tuple(resultRelInfo, elemTupleSlot, estate);
-
-    if (node->variable_name != NULL)
-        css->tuple_info = add_tuple_info(css->tuple_info, tuple, node->variable_name);
+    insert_entity_tuple(resultRelInfo, elemTupleSlot, estate);
 
     /*
      * When the edge is used by clauses higher in the execution tree
@@ -458,8 +447,6 @@ static Datum create_vertex(cypher_create_custom_scan_state *css,
      */
     if (CYPHER_TARGET_NODE_INSERT_ENTITY(node->flags))
     {
-        HeapTuple tuple;
-
         /*
          * Set estate's result relation to the vertex's result
          * relation.
@@ -482,25 +469,7 @@ static Datum create_vertex(cypher_create_custom_scan_state *css,
             scanTupleSlot->tts_isnull[node->prop_attr_num];
 
         // Insert the new vertex
-        tuple = insert_entity_tuple(resultRelInfo, elemTupleSlot, estate);
-
-        /*
-         * If this vertex is a variable store the newly created tuple in
-         * the CustomScanState. This will tell future clauses what the
-         * tuple is for this variable, which is needed if the query wants
-         * to update this tuple.
-         */
-        if (node->variable_name != NULL)
-        {
-            clause_tuple_information *tuple_info;
-
-            tuple_info = palloc(sizeof(clause_tuple_information));
-
-            tuple_info->tuple = tuple;
-            tuple_info->name = node->variable_name;
-
-            css->tuple_info = lappend(css->tuple_info, tuple_info);
-        }
+        insert_entity_tuple(resultRelInfo, elemTupleSlot, estate);
 
         /*
          * When the vertex is used by clauses higher in the execution tree
@@ -579,11 +548,7 @@ static Datum create_vertex(cypher_create_custom_scan_state *css,
          */
         if (!SAFE_TO_SKIP_EXISTENCE_CHECK(node->flags))
         {
-            bool is_deleted = false;
-
-            get_heap_tuple(&css->css, node->variable_name, &is_deleted);
-
-            if (is_deleted || !entity_exists(estate, css->graph_oid, DATUM_GET_GRAPHID(id)))
+            if (!entity_exists(estate, css->graph_oid, DATUM_GET_GRAPHID(id)))
                 ereport(ERROR,
                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                      errmsg("vertex assigned to variable %s was deleted", node->variable_name)));
diff --git a/src/backend/executor/cypher_delete.c b/src/backend/executor/cypher_delete.c
index c0fe945..3987688 100644
--- a/src/backend/executor/cypher_delete.c
+++ b/src/backend/executor/cypher_delete.c
@@ -56,8 +56,7 @@ static void find_connected_edges(CustomScanState *node, char *graph_name, List *
                                  char *var_name, graphid id, bool detach_delete);
 static agtype_value *extract_entity(CustomScanState *node, TupleTableSlot *scanTupleSlot,
                                     int entity_position);
-static void delete_entity(CustomScanState *node, char *graph_name,
-                          char *label_name, HeapTuple tuple);
+static void delete_entity(EState *estate, ResultRelInfo *resultRelInfo, HeapTuple tuple);
 
 const CustomExecMethods cypher_delete_exec_methods = {DELETE_SCAN_STATE_NAME,
                                                       begin_cypher_delete,
@@ -161,8 +160,6 @@ static TupleTableSlot *exec_cypher_delete(CustomScanState *node)
             econtext->ecxt_scantuple =
                 node->ss.ps.lefttree->ps_ProjInfo->pi_exprContext->ecxt_scantuple;
 
-            css->tuple_info = NIL;
-
             process_delete_list(node);
         }
 
@@ -182,8 +179,6 @@ static TupleTableSlot *exec_cypher_delete(CustomScanState *node)
         econtext->ecxt_scantuple =
             node->ss.ps.lefttree->ps_ProjInfo->pi_exprContext->ecxt_scantuple;
 
-        css->tuple_info = NIL;
-
         process_delete_list(node);
 
         econtext->ecxt_scantuple =
@@ -270,31 +265,24 @@ static agtype_value *extract_entity(CustomScanState *node, TupleTableSlot *scanT
 
     if (original_entity_value->type != AGTV_VERTEX && original_entity_value->type != AGTV_EDGE)
         ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                errmsg("DELETE clause can only update vertex and edges")));
+                errmsg("DELETE clause can only delete vertices and edges")));
 
     return original_entity_value;
 }
 
 /*
  * Try and delete the entity that is describe by the HeapTuple in
- * the table described by the graph_name and label_name.
+ * the table described by the resultRelInfo.
  */
-static void delete_entity(CustomScanState *node, char *graph_name,
-                          char *label_name, HeapTuple tuple)
+static void delete_entity(EState *estate, ResultRelInfo *resultRelInfo, HeapTuple tuple)
 {
-    cypher_delete_custom_scan_state *css =
-        (cypher_delete_custom_scan_state *)node;
-    EState *estate = css->css.ss.ps.state;
-    ResultRelInfo *resultRelInfo, *saved_resultRelInfo;
+    ResultRelInfo *saved_resultRelInfo;
     LockTupleMode lockmode;
     HeapUpdateFailureData hufd;
     HTSU_Result lock_result;
     HTSU_Result delete_result;
     Buffer buffer;
 
-    resultRelInfo = create_entity_result_rel_info(estate, graph_name, label_name);
-    ExecOpenIndices(resultRelInfo, false);
-
     // Find the physical tuple, this variable is coming from
     saved_resultRelInfo = estate->es_result_relation_info;
     estate->es_result_relation_info = resultRelInfo;
@@ -352,9 +340,6 @@ static void delete_entity(CustomScanState *node, char *graph_name,
     ReleaseBuffer(buffer);
 
     estate->es_result_relation_info = saved_resultRelInfo;
-
-    ExecCloseIndices(resultRelInfo);
-    heap_close(resultRelInfo->ri_RelationDesc, RowExclusiveLock);
 }
 
 /*
@@ -368,16 +353,19 @@ static void process_delete_list(CustomScanState *node)
     ListCell *lc;
     ExprContext *econtext = css->css.ss.ps.ps_ExprContext;
     TupleTableSlot *scanTupleSlot = econtext->ecxt_scantuple;
+    EState *estate = node->ss.ps.state;
 
     foreach(lc, css->delete_data->delete_items)
     {
         cypher_delete_item *item;
         agtype_value *original_entity_value, *id, *label;
+        ScanKeyData scan_keys[1];
+        HeapScanDesc scan_desc;
+        ResultRelInfo *resultRelInfo;
         HeapTuple heap_tuple;
         char *label_name;
         Value *pos;
         int entity_position;
-        bool is_deleted;
 
         item = lfirst(lc);
 
@@ -388,39 +376,69 @@ static void process_delete_list(CustomScanState *node)
         if (scanTupleSlot->tts_isnull[entity_position - 1])
             continue;
 
-        /*
-         * find the where the entity came from, if the tuple was deleted
-         * by a previous DELETE clause its safe to skip this tuple
-         */
-        heap_tuple = get_heap_tuple(node, item->var_name, &is_deleted);
-        if (is_deleted || heap_tuple == NULL)
-            continue;
-
         original_entity_value = extract_entity(node, scanTupleSlot, entity_position);
 
         id = get_agtype_value_object_value(original_entity_value, "id");
         label = get_agtype_value_object_value(original_entity_value, "label");
         label_name = pnstrdup(label->val.string.val, label->val.string.len);
 
+        resultRelInfo = create_entity_result_rel_info(estate, css->delete_data->graph_name, label_name);
+
+        // Setup the scan key to require the id field on-disc to match the entity's graphid.
+        if (original_entity_value->type == AGTV_VERTEX)
+        {
+            ScanKeyInit(&scan_keys[0], Anum_ag_label_vertex_table_id, BTEqualStrategyNumber,
+                        F_GRAPHIDEQ, GRAPHID_GET_DATUM(id->val.int_value));
+        }
+        else if (original_entity_value->type == AGTV_EDGE)
+        {
+            ScanKeyInit(&scan_keys[0], Anum_ag_label_edge_table_id, BTEqualStrategyNumber,
+                        F_GRAPHIDEQ, GRAPHID_GET_DATUM(id->val.int_value));
+
+        }
+        else
+        {
+            ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                    errmsg("DELETE clause can only delete vertices and edges")));
+        }
+
+        // Setup the scan description, with the correct snapshot and scan keys.
+        scan_desc = heap_beginscan(resultRelInfo->ri_RelationDesc, estate->es_snapshot, 1, scan_keys);
+
+        // Retrieve the tuple.
+        heap_tuple = heap_getnext(scan_desc, ForwardScanDirection);
+
+        /*
+         * If the heap tuple still exists (It wasn't deleted after this variable was created)
+         * we can delete it. Otherwise, its safe to skip this delete
+         */
+        if (!HeapTupleIsValid(heap_tuple))
+        {
+            heap_endscan(scan_desc);
+            heap_close(resultRelInfo->ri_RelationDesc, RowExclusiveLock);
+
+            continue;
+        }
+
         /*
          * For vertices, we need to check if the vertex is connected to any edges,
-         * if there are, we need to either delete them, or throw an error, depending
-         * on if the query specified the DETACH option.
+         * if there are, we need to delete them or throw an error, depending on if
+         * the query specified the DETACH option.
          */
         if (original_entity_value->type == AGTV_VERTEX)
+        {
             find_connected_edges(node, css->delete_data->graph_name, css->edge_labels,
                                  item->var_name, id->val.int_value, css->delete_data->detach);
+        }
 
         /*
          * At this point, we are ready to delete the node/vertex.
          */
-        delete_entity(node, css->delete_data->graph_name, label_name, heap_tuple);
+        delete_entity(estate, resultRelInfo, heap_tuple);
 
-        /*
-         * Add the deleted tuple to the custom scan state's info on updated
-         * tuples.
-         */
-        css->tuple_info = add_tuple_info(css->tuple_info, heap_tuple, item->var_name);
+        // Close the scan and the relation. 
+        heap_endscan(scan_desc);
+        heap_close(resultRelInfo->ri_RelationDesc, RowExclusiveLock);
     }
 }
 
@@ -487,7 +505,7 @@ static void find_connected_edges(CustomScanState *node, char *graph_name, List *
                  * specified in the query.
                  */
                 if (detach_delete)
-                    delete_entity(node, graph_name, label_name, tuple);
+                    delete_entity(estate, resultRelInfo, tuple);
                 else
                     ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),
                             errmsg("Cannot delete vertex %s, because it still has edges attached. "
diff --git a/src/backend/executor/cypher_set.c b/src/backend/executor/cypher_set.c
index e2d8e21..9f9254b 100644
--- a/src/backend/executor/cypher_set.c
+++ b/src/backend/executor/cypher_set.c
@@ -170,8 +170,6 @@ static void process_all_tuples(CustomScanState *node)
 
     do
     {
-        css->tuple_info = NIL;
-
         process_update_list(node);
         Decrement_Estate_CommandId(estate)
         slot = ExecProcNode(node->ss.ps.lefttree);
@@ -284,19 +282,20 @@ static void process_update_list(CustomScanState *node)
     ListCell *lc;
     EState *estate = css->css.ss.ps.state;
 
-    css->tuple_info = NIL;
-
     foreach (lc, css->set_list->set_items)
     {
         agtype_value *altered_properties, *original_entity_value, *original_properties, *id, *label;
         agtype *original_entity, *new_property_value;
+        TupleTableSlot *slot;
+        ResultRelInfo *resultRelInfo;
+        ScanKeyData scan_keys[1];
+        HeapScanDesc scan_desc;
         bool remove_property;
         char *label_name;
         cypher_update_item *update_item = (cypher_update_item *)lfirst(lc);
         Datum new_entity;
         HeapTuple heap_tuple;
         char *clause_name = css->set_list->clause_name;
-        bool is_deleted;
 
         update_item = (cypher_update_item *)lfirst(lc);
 
@@ -327,26 +326,45 @@ static void process_update_list(CustomScanState *node)
         original_properties = get_agtype_value_object_value(original_entity_value, "properties");
 
         /*
-         * Determine if the property should be removed.
+         * Determine if the property should be removed. This will be because this is a REMOVE clause
+         * or the variable references a variable that is NULL. It will be possible for a variable to
+         * be NULL when OPTIONAL MATCH is implemented.
          */
         if(update_item->remove_item)
             remove_property = true;
         else
             remove_property = scanTupleSlot->tts_isnull[update_item->prop_position - 1];
 
+        /*
+         * If we need to remove the property, set the value to NULL. Otherwise fetch the
+         * evaluated expression from the tuble slot.
+         */
         if (remove_property)
             new_property_value = NULL;
         else
             new_property_value = DATUM_GET_AGTYPE_P(scanTupleSlot->tts_values[update_item->prop_position - 1]);
 
+        /*
+         * Alter the properties Agtype value to contain or remove the updated property.
+         */
         altered_properties = alter_property_value(original_properties, update_item->prop_name,
                                                   new_property_value, remove_property);
 
+        resultRelInfo = create_entity_result_rel_info(estate, css->set_list->graph_name, label_name);
+
+        slot = ExecInitExtraTupleSlot(estate, RelationGetDescr(resultRelInfo->ri_RelationDesc));
+
+        /*
+         *  Now that we have the updated properties, create a either a vertex or edge Datum for the in-memory
+         *  update, and setup the tupleTableSlot for the on-disc update.
+         */
         if (original_entity_value->type == AGTV_VERTEX)
         {
             new_entity = make_vertex(GRAPHID_GET_DATUM(id->val.int_value),
                                      CStringGetDatum(label_name),
                                      AGTYPE_P_GET_DATUM(agtype_value_to_agtype(altered_properties)));
+
+            slot = populate_vertex_tts(slot, id, altered_properties);
         }
         else if (original_entity_value->type == AGTV_EDGE)
         {
@@ -358,6 +376,8 @@ static void process_update_list(CustomScanState *node)
                                    GRAPHID_GET_DATUM(endid->val.int_value),
                                    CStringGetDatum(label_name),
                                    AGTYPE_P_GET_DATUM(agtype_value_to_agtype(altered_properties)));
+
+            slot = populate_edge_tts(slot, id, startid, endid, altered_properties);
         }
 	else
 	{
@@ -365,47 +385,37 @@ static void process_update_list(CustomScanState *node)
                     errmsg("age %s clause can only update vertex and edges", clause_name)));
 	}
 
-        // update the in-memory tuple slot
+        // place the datum in its tuple table slot position.
         scanTupleSlot->tts_values[update_item->entity_position - 1] = new_entity;
 
+        /*
+         *  If the tuple table slot has paths, we need to inspect them to see if the updated
+         *  entity is contained within them and replace the entity if it is.
+         */
         update_all_paths(node, id->val.int_value, DATUM_GET_AGTYPE_P(new_entity));
 
-        // update the on-disc table
-        heap_tuple = get_heap_tuple(node, update_item->var_name, &is_deleted);
-
-        if (!is_deleted)
-        {
-            ResultRelInfo *resultRelInfo;
-            TupleTableSlot *elemTupleSlot;
-            HeapTuple tuple;
-
-            resultRelInfo = create_entity_result_rel_info(estate, css->set_list->graph_name, label_name);
-
-            ExecOpenIndices(resultRelInfo, false);
-
-            elemTupleSlot = ExecInitExtraTupleSlot(estate, RelationGetDescr(resultRelInfo->ri_RelationDesc));
-            if (original_entity_value->type == AGTV_VERTEX)
-            {
-                elemTupleSlot = populate_vertex_tts(elemTupleSlot, id, altered_properties);
-            }
-            else if (original_entity_value->type == AGTV_EDGE)
-            {
-                agtype_value *startid = get_agtype_value_object_value(original_entity_value, "start_id");
-                agtype_value *endid = get_agtype_value_object_value(original_entity_value, "end_id");
+        // Setup the scan key to require the id field on-disc to match the entity's graphid.
+        ScanKeyInit(&scan_keys[0], 1, BTEqualStrategyNumber, F_GRAPHIDEQ, GRAPHID_GET_DATUM(id->val.int_value));
 
-                elemTupleSlot = populate_edge_tts(elemTupleSlot, id, startid, endid, altered_properties);
-            }
+        // Setup the scan description, with the correct snapshot and scan keys.
+        scan_desc = heap_beginscan(resultRelInfo->ri_RelationDesc, estate->es_snapshot, 1, scan_keys);
 
-            tuple = update_entity_tuple(resultRelInfo, elemTupleSlot, estate, heap_tuple);
+        // Retrieve the tuple.
+        heap_tuple = heap_getnext(scan_desc, ForwardScanDirection);
 
-            if (update_item->var_name != NULL)
-                css->tuple_info = add_tuple_info(css->tuple_info, tuple, update_item->var_name);
-
-            ExecCloseIndices(resultRelInfo);
+        /*
+         * If the heap tuple still exists (It wasn't deleted between the match and this SET/REMOVE) 
+         * update the heap_tuple
+         */
+        if(HeapTupleIsValid(heap_tuple))
+        {
+            heap_tuple = update_entity_tuple(resultRelInfo, slot, estate, heap_tuple);
+        }
 
-            heap_close(resultRelInfo->ri_RelationDesc, RowExclusiveLock);
+        // close the ScanDescription and the Relation 
+        heap_endscan(scan_desc);
+        heap_close(resultRelInfo->ri_RelationDesc, RowExclusiveLock);
 
-        }
     }
 }
 
@@ -420,8 +430,6 @@ static TupleTableSlot *exec_cypher_set(CustomScanState *node)
 
     saved_resultRelInfo = estate->es_result_relation_info;
 
-    css->tuple_info = NIL;
-
     //Process the subtree first
     Decrement_Estate_CommandId(estate);
     slot = ExecProcNode(node->ss.ps.lefttree);
diff --git a/src/backend/executor/cypher_utils.c b/src/backend/executor/cypher_utils.c
index 159ba0e..d05e3a3 100644
--- a/src/backend/executor/cypher_utils.c
+++ b/src/backend/executor/cypher_utils.c
@@ -40,265 +40,34 @@
 #include "utils/agtype.h"
 #include "utils/graphid.h"
 
-static bool find_scan_state_walker(PlanState *p, void *context);
-static bool inspect_clause_tuple_info(List *tuple_info, char *var_name);
-
-typedef struct find_scan_state_context {
-    char *var_name;
-    PlanState *variable_plan_state;
-    EState *estate;
-    bool is_deleted;
-} find_scan_state_context;
-
-/*
- * This function will find the scan state that a variable name
- * is referencing.
- */
-PlanState *find_plan_state(CustomScanState *node, char *var_name, bool *is_deleted)
-{
-    PlanState *ps;
-    find_scan_state_context *context;
-
-    context = palloc(sizeof(find_scan_state_context));
-    context->var_name = var_name;
-    context->estate = node->ss.ps.state;
-    context->is_deleted = false;
-    context->variable_plan_state = NULL;
-
-    planstate_tree_walker((PlanState *)node, find_scan_state_walker, context);
-
-    ps = context->variable_plan_state;
-
-    if (ps == NULL)
-        ereport(ERROR,
-            (errmsg("cannot find plan state for variable '%s'", var_name)));
-
-    *is_deleted = context->is_deleted;
-
-    return ps;
-}
-
-
-static bool find_scan_state_walker(PlanState *p, void *context)
-{
-    find_scan_state_context *cnxt = (find_scan_state_context *)context;
-    EState *estate = cnxt->estate;
-    char *var_name = cnxt->var_name;
-
-    switch (p->type)
-    {
-        case T_CustomScanState:
-        {
-            CustomScanState *css = (CustomScanState *)p;
-            const struct CustomExecMethods *methods = css->methods;
-
-            if (!strcmp(methods->CustomName, CREATE_SCAN_STATE_NAME))
-            {
-                cypher_create_custom_scan_state *custom_state = (cypher_create_custom_scan_state *)css;
-                if (inspect_clause_tuple_info(custom_state->tuple_info, var_name))
-                {
-                    cnxt->variable_plan_state = (PlanState *)p;
-                    return true;
-                }
-            }
-            else if (!strcmp(methods->CustomName, SET_SCAN_STATE_NAME))
-            {
-                cypher_set_custom_scan_state *custom_state = (cypher_set_custom_scan_state *)css;
-                if (inspect_clause_tuple_info(custom_state->tuple_info, var_name))
-                {
-                    cnxt->variable_plan_state = (PlanState *)p;
-                    return true;
-                }
-            }
-            else if (!strcmp(methods->CustomName, DELETE_SCAN_STATE_NAME))
-            {
-                cypher_delete_custom_scan_state *custom_state = (cypher_delete_custom_scan_state *)css;
-                if (inspect_clause_tuple_info(custom_state->tuple_info, var_name))
-                {
-                    cnxt->is_deleted = true;
-                    cnxt->variable_plan_state = (PlanState *)p;
-                    return true;
-                }
-            }
-            break;
-        }
-        case T_AppendState:
-        {
-            if (planstate_tree_walker(p, find_scan_state_walker, context))
-            {
-                AppendState *append = (AppendState *)p;
-                cnxt->variable_plan_state = (PlanState *)append->appendplans[append->as_whichplan];
-                return true;
-            }
-            else
-            {
-                return false;
-            }
-        }
-        case T_SeqScanState:
-        {
-            SeqScanState *seq = (SeqScanState *)p;
-            SeqScan *scan = (SeqScan *)seq->ss.ps.plan;
-            RangeTblEntry *rte = rt_fetch(scan->scanrelid, estate->es_range_table);
-            Alias *alias = rte->alias;
-
-            if (!strcmp(alias->aliasname, var_name))
-            {
-                cnxt->variable_plan_state = (PlanState *)seq;
-                return true;
-            }
-
-            return false;
-        }
-        default:
-            break;
-    }
-
-    return planstate_tree_walker(p, find_scan_state_walker, context);
-}
-
-/*
- * In the custom scan states we need to find the heap tuple that we want to
- * modify (CREATE, DELETE, SET, etc). Find the scan state that has most recently
- * modified, if no clause have finds the heap tuple in the original scan state.
- */
-HeapTuple get_heap_tuple(CustomScanState *node, char *var_name, bool *is_deleted)
-{
-    PlanState *ps = find_plan_state(node, var_name, is_deleted);
-    HeapTuple heap_tuple = NULL;
-
-    switch (ps->type)
-    {
-        case T_CustomScanState:
-        {
-            CustomScanState *css = (CustomScanState *)ps;
-            const struct CustomExecMethods *methods = css->methods;
-            List *tuple_info;
-            ListCell *lc;
-
-            if (!strcmp(methods->CustomName, CREATE_SCAN_STATE_NAME))
-            {
-                cypher_create_custom_scan_state *css = (cypher_create_custom_scan_state *)ps;
-                tuple_info = css->tuple_info;
-            }
-            else if (!strcmp(methods->CustomName, SET_SCAN_STATE_NAME))
-            {
-                cypher_set_custom_scan_state *css = (cypher_set_custom_scan_state *)ps;
-                tuple_info = css->tuple_info;
-            }
-            else if (!strcmp(methods->CustomName, DELETE_SCAN_STATE_NAME))
-            {
-                cypher_delete_custom_scan_state *css = (cypher_delete_custom_scan_state *)ps;
-                tuple_info = css->tuple_info;
-            }
-            else
-            {
-                ereport(ERROR, (errmsg("cannot extract tuple information from %s", methods->CustomName)));
-            }
-
-            foreach(lc, tuple_info)
-            {
-                clause_tuple_information *info = lfirst(lc);
-
-                if (!strcmp(info->name, var_name))
-                {
-                    heap_tuple = info->tuple;
-                    break;
-                }
-            }
-            break;
-        }
-        case T_SeqScanState:
-        {
-            bool isNull;
-            TupleTableSlot *ss_tts = ((SeqScanState *)ps)->ss.ss_ScanTupleSlot;
-
-            if (!ss_tts->tts_tuple)
-            {
-                heap_tuple = ss_tts->tts_tuple;
-                break;
-            }
-
-            heap_getsysattr(ss_tts->tts_tuple, SelfItemPointerAttributeNumber,
-                            ss_tts->tts_tupleDescriptor, &isNull);
-
-            if (isNull)
-                ereport(ERROR, (errmsg("cypher cannot find entity to update")));
-
-            heap_tuple = ss_tts->tts_tuple;
-            break;
-        }
-        default:
-            ereport(ERROR, (errmsg("cannot extract heap tuple from scan state")));
-            break;
-    }
-
-    return heap_tuple;
-}
-
-static bool inspect_clause_tuple_info(List *tuple_info, char *var_name)
-{
-    ListCell *lc;
-
-    foreach(lc, tuple_info)
-    {
-        clause_tuple_information *info = lfirst(lc);
-
-        if (!strcmp(info->name, var_name))
-            return true;
-    }
-    return false;
-}
-
 ResultRelInfo *create_entity_result_rel_info(EState *estate, char *graph_name, char *label_name)
 {
-    Oid relid;
     RangeVar *rv;
-    Relation label_relation, rel;
+    Relation label_relation;
     ResultRelInfo *resultRelInfo;
 
     ParseState *pstate = make_parsestate(NULL);
 
+    resultRelInfo = palloc(sizeof(ResultRelInfo));
+
     if (strlen(label_name) == 0)
+    {
         rv = makeRangeVar(graph_name, AG_DEFAULT_LABEL_VERTEX, -1);
+    }
     else
+    {
         rv = makeRangeVar(graph_name, label_name, -1);
-    label_relation = parserOpenTable(pstate, rv, RowExclusiveLock);
-
-    relid = RelationGetRelid(label_relation);
-
-    heap_close(label_relation, NoLock);
-
-    free_parsestate(pstate);
+    }
 
-    rel = heap_open(relid, RowExclusiveLock);
-    resultRelInfo = palloc(sizeof(ResultRelInfo));
+    label_relation = parserOpenTable(pstate, rv, RowExclusiveLock);
 
-    InitResultRelInfo(resultRelInfo, rel,
+    InitResultRelInfo(resultRelInfo, label_relation,
                       list_length(estate->es_range_table), NULL,
                       estate->es_instrument);
 
-    return resultRelInfo;
-}
-
-ItemPointer get_self_item_pointer(TupleTableSlot *tts)
-{
-    ItemPointer ip;
-    Datum d;
-    bool isNull;
-
-    if (!tts->tts_tuple)
-        ereport(ERROR, (errmsg("cypher update clause needs physical tuples")));
-
-    d = heap_getsysattr(tts->tts_tuple, SelfItemPointerAttributeNumber,
-                        tts->tts_tupleDescriptor, &isNull);
-
-    if (isNull)
-        ereport(ERROR, (errmsg("cypher cannot find entity to update")));
-
-    ip = (ItemPointer)DatumGetPointer(d);
+    free_parsestate(pstate);
 
-    return ip;
+    return resultRelInfo;
 }
 
 TupleTableSlot *populate_vertex_tts(
@@ -360,20 +129,3 @@ TupleTableSlot *populate_edge_tts(
 
     return elemTupleSlot;
 }
-
-/*
- * Create a new clause_tuple_information with the given
- * heap tuple and variable name and append it to the given
- * list
- */
-List *add_tuple_info(List *list, HeapTuple heap_tuple, char *var_name)
-{
-    clause_tuple_information *tuple_info;
-
-    tuple_info = palloc(sizeof(clause_tuple_information));
-
-    tuple_info->tuple = heap_tuple;
-    tuple_info->name = var_name;
-
-    return lappend(list, tuple_info);
-}
diff --git a/src/backend/parser/cypher_analyze.c b/src/backend/parser/cypher_analyze.c
index 171b726..b6e158c 100644
--- a/src/backend/parser/cypher_analyze.c
+++ b/src/backend/parser/cypher_analyze.c
@@ -402,7 +402,8 @@ static void convert_cypher_to_subquery(RangeTblEntry *rte, ParseState *pstate)
      * coercion logic applied to them because we are forcing the column
      * definition list to be a particular way in this case.
      */
-    if (is_ag_node(llast(stmt), cypher_create)  || is_ag_node(llast(stmt), cypher_set))
+    if (is_ag_node(llast(stmt), cypher_create)  || is_ag_node(llast(stmt), cypher_set) ||
+        is_ag_node(llast(stmt), cypher_delete))
     {
         // column definition list must be ... AS relname(colname agtype) ...
         if (!(rtfunc->funccolcount == 1 &&
diff --git a/src/include/executor/cypher_utils.h b/src/include/executor/cypher_utils.h
index 8e9eb5d..bd57f9d 100644
--- a/src/include/executor/cypher_utils.h
+++ b/src/include/executor/cypher_utils.h
@@ -47,23 +47,12 @@
     estate->es_output_cid--; \
     estate->es_snapshot->curcid--;
 
-/*
- * This holds information in clauses that create or alter tuples on
- * disc, this is so future clause can manipulate those tuples if
- * necessary.
- */
-typedef struct clause_tuple_information {
-    char *name;
-    HeapTuple tuple;
-} clause_tuple_information;
-
 typedef struct cypher_create_custom_scan_state
 {
     CustomScanState css;
     CustomScan *cs;
     List *pattern;
     List *path_values;
-    List *tuple_info;
     uint32 flags;
     TupleTableSlot *slot;
     Oid graph_oid;
@@ -74,7 +63,6 @@ typedef struct cypher_set_custom_scan_state
     CustomScanState css;
     CustomScan *cs;
     cypher_update_information *set_list;
-    List *tuple_info;
     int flags;
 } cypher_set_custom_scan_state;
 
@@ -84,19 +72,14 @@ typedef struct cypher_delete_custom_scan_state
     CustomScan *cs;
     cypher_delete_information *delete_data;
     int flags;
-    List *tuple_info;
     List *edge_labels;
 } cypher_delete_custom_scan_state;
 
-PlanState *find_plan_state(CustomScanState *node, char *var_name, bool *is_deleted);
-
 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);
-List *add_tuple_info(List *list, HeapTuple heap_tuple, char *var_name);
-ItemPointer get_self_item_pointer(TupleTableSlot *tts);
-HeapTuple get_heap_tuple(CustomScanState *node, char *var_name, bool *is_deleted);
+
 #endif