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 18:54:24 UTC

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

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 f10be62  Revert "Refactor HeapTuple Retrieval Logic"
f10be62 is described below

commit f10be627ab470fe7a12935e138e38cd3582496ae
Author: Josh Innis <Jo...@gmail.com>
AuthorDate: Thu Jun 24 11:47:06 2021 -0700

    Revert "Refactor HeapTuple Retrieval Logic"
    
    This reverts commit 1dea9e7e3eacba3da63ab0c98d28d77616804136.
    
    The patch 'Refactor HeapTuple Retrieval Logic' was pushed incorrectly.
---
 regress/expected/cypher_delete.out   |  85 +----------
 regress/sql/cypher_delete.sql        |  28 +---
 src/backend/executor/cypher_create.c |  34 ++++-
 src/backend/executor/cypher_delete.c |  94 +++++--------
 src/backend/executor/cypher_set.c    |  87 +++++-------
 src/backend/executor/cypher_utils.c  | 266 +++++++++++++++++++++++++++++++++--
 src/backend/parser/cypher_analyze.c  |   3 +-
 src/include/executor/cypher_utils.h  |  19 ++-
 8 files changed, 389 insertions(+), 227 deletions(-)

diff --git a/regress/expected/cypher_delete.out b/regress/expected/cypher_delete.out
index cc268c5..0b0980b 100644
--- a/regress/expected/cypher_delete.out
+++ b/regress/expected/cypher_delete.out
@@ -18,6 +18,8 @@
  */
 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 
@@ -494,97 +496,16 @@ 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 5 other objects
+NOTICE:  drop cascades to 4 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 2937a23..f062722 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,32 +196,6 @@ 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 fb30b66..c7aeb30 100644
--- a/src/backend/executor/cypher_create.c
+++ b/src/backend/executor/cypher_create.c
@@ -154,6 +154,8 @@ 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);
@@ -220,6 +222,8 @@ 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);
         }
 
@@ -239,6 +243,8 @@ 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 =
@@ -304,6 +310,7 @@ 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;
 
@@ -395,6 +402,9 @@ static void create_edge(cypher_create_custom_scan_state *css,
     // 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);
+
     /*
      * When the edge is used by clauses higher in the execution tree
      * we need to create an edge datum. When the edge is a variable,
@@ -475,6 +485,24 @@ static Datum create_vertex(cypher_create_custom_scan_state *css,
         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);
+        }
+
+        /*
          * When the vertex is used by clauses higher in the execution tree
          * we need to create a vertex datum. When the vertex is a variable,
          * add to the scantuple slot. When the vertex is part of a path
@@ -551,7 +579,11 @@ static Datum create_vertex(cypher_create_custom_scan_state *css,
          */
         if (!SAFE_TO_SKIP_EXISTENCE_CHECK(node->flags))
         {
-            if (!entity_exists(estate, css->graph_oid, DATUM_GET_GRAPHID(id)))
+            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)))
                 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 3987688..c0fe945 100644
--- a/src/backend/executor/cypher_delete.c
+++ b/src/backend/executor/cypher_delete.c
@@ -56,7 +56,8 @@ 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(EState *estate, ResultRelInfo *resultRelInfo, HeapTuple tuple);
+static void delete_entity(CustomScanState *node, char *graph_name,
+                          char *label_name, HeapTuple tuple);
 
 const CustomExecMethods cypher_delete_exec_methods = {DELETE_SCAN_STATE_NAME,
                                                       begin_cypher_delete,
@@ -160,6 +161,8 @@ 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);
         }
 
@@ -179,6 +182,8 @@ 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 =
@@ -265,24 +270,31 @@ 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 delete vertices and edges")));
+                errmsg("DELETE clause can only update vertex and edges")));
 
     return original_entity_value;
 }
 
 /*
  * Try and delete the entity that is describe by the HeapTuple in
- * the table described by the resultRelInfo.
+ * the table described by the graph_name and label_name.
  */
-static void delete_entity(EState *estate, ResultRelInfo *resultRelInfo, HeapTuple tuple)
+static void delete_entity(CustomScanState *node, char *graph_name,
+                          char *label_name, HeapTuple tuple)
 {
-    ResultRelInfo *saved_resultRelInfo;
+    cypher_delete_custom_scan_state *css =
+        (cypher_delete_custom_scan_state *)node;
+    EState *estate = css->css.ss.ps.state;
+    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;
@@ -340,6 +352,9 @@ static void delete_entity(EState *estate, ResultRelInfo *resultRelInfo, HeapTupl
     ReleaseBuffer(buffer);
 
     estate->es_result_relation_info = saved_resultRelInfo;
+
+    ExecCloseIndices(resultRelInfo);
+    heap_close(resultRelInfo->ri_RelationDesc, RowExclusiveLock);
 }
 
 /*
@@ -353,19 +368,16 @@ 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);
 
@@ -376,69 +388,39 @@ 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 delete them or throw an error, depending on if
-         * the query specified the DETACH option.
+         * if there are, we need to either 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(estate, resultRelInfo, heap_tuple);
+        delete_entity(node, css->delete_data->graph_name, label_name, heap_tuple);
 
-        // Close the scan and the relation. 
-        heap_endscan(scan_desc);
-        heap_close(resultRelInfo->ri_RelationDesc, RowExclusiveLock);
+        /*
+         * 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);
     }
 }
 
@@ -505,7 +487,7 @@ static void find_connected_edges(CustomScanState *node, char *graph_name, List *
                  * specified in the query.
                  */
                 if (detach_delete)
-                    delete_entity(estate, resultRelInfo, tuple);
+                    delete_entity(node, graph_name, label_name, 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 9523b3f..e2d8e21 100644
--- a/src/backend/executor/cypher_set.c
+++ b/src/backend/executor/cypher_set.c
@@ -170,6 +170,8 @@ 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);
@@ -282,14 +284,12 @@ 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);
@@ -327,45 +327,26 @@ 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. 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.
+         * Determine if the property should be removed.
          */
         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)
         {
@@ -377,8 +358,6 @@ 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
 	{
@@ -386,37 +365,47 @@ static void process_update_list(CustomScanState *node)
                     errmsg("age %s clause can only update vertex and edges", clause_name)));
 	}
 
-        // place the datum in its tuple table slot position.
+        // update the in-memory tuple slot
         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));
 
-        // 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));
+        // 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;
 
-        // Setup the scan description, with the correct snapshot and scan keys.
-        scan_desc = heap_beginscan(resultRelInfo->ri_RelationDesc, estate->es_snapshot, 1, scan_keys);
+            resultRelInfo = create_entity_result_rel_info(estate, css->set_list->graph_name, label_name);
 
-        // Retrieve the tuple.
-        heap_tuple = heap_getnext(scan_desc, ForwardScanDirection);
+            ExecOpenIndices(resultRelInfo, false);
 
-        /*
-         * 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);
-        }
+            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");
 
-        // close the ScanDescription and the Relation 
-        heap_endscan(scan_desc);
-        heap_close(resultRelInfo->ri_RelationDesc, RowExclusiveLock);
+                elemTupleSlot = populate_edge_tts(elemTupleSlot, id, startid, endid, altered_properties);
+            }
+
+            tuple = update_entity_tuple(resultRelInfo, elemTupleSlot, estate, heap_tuple);
+
+            if (update_item->var_name != NULL)
+                css->tuple_info = add_tuple_info(css->tuple_info, tuple, update_item->var_name);
+
+            ExecCloseIndices(resultRelInfo);
 
+            heap_close(resultRelInfo->ri_RelationDesc, RowExclusiveLock);
+
+        }
     }
 }
 
@@ -431,6 +420,8 @@ 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 92eb0ce..159ba0e 100644
--- a/src/backend/executor/cypher_utils.c
+++ b/src/backend/executor/cypher_utils.c
@@ -38,6 +38,217 @@
 #include "executor/cypher_executor.h"
 #include "executor/cypher_utils.h"
 #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)
 {
@@ -48,30 +259,48 @@ ResultRelInfo *create_entity_result_rel_info(EState *estate, char *graph_name, c
 
     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);
 
-    InitResultRelInfo(resultRelInfo, label_relation,
-                      list_length(estate->es_range_table), NULL,
-                      estate->es_instrument);
+    heap_close(label_relation, NoLock);
 
     free_parsestate(pstate);
 
+    rel = heap_open(relid, RowExclusiveLock);
+    resultRelInfo = palloc(sizeof(ResultRelInfo));
+
+    InitResultRelInfo(resultRelInfo, rel,
+                      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);
+
+    return ip;
+}
+
 TupleTableSlot *populate_vertex_tts(
     TupleTableSlot *elemTupleSlot, agtype_value *id, agtype_value *properties)
 {
@@ -131,3 +360,20 @@ 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 b6e158c..171b726 100644
--- a/src/backend/parser/cypher_analyze.c
+++ b/src/backend/parser/cypher_analyze.c
@@ -402,8 +402,7 @@ 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) ||
-        is_ag_node(llast(stmt), cypher_delete))
+    if (is_ag_node(llast(stmt), cypher_create)  || is_ag_node(llast(stmt), cypher_set))
     {
         // 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 bd57f9d..8e9eb5d 100644
--- a/src/include/executor/cypher_utils.h
+++ b/src/include/executor/cypher_utils.h
@@ -47,12 +47,23 @@
     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;
@@ -63,6 +74,7 @@ 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;
 
@@ -72,14 +84,19 @@ 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