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/04/20 23:26:28 UTC

[incubator-age] branch master updated: Refactor Create Clause

This is an automated email from the ASF dual-hosted git repository.

joshinnis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-age.git


The following commit(s) were added to refs/heads/master by this push:
     new 1f15772  Refactor Create Clause
1f15772 is described below

commit 1f15772693be43618854b9c6e80e3fada7f682df
Author: Josh Innis <Jo...@gmail.com>
AuthorDate: Mon Apr 19 09:46:15 2021 -0700

    Refactor Create Clause
    
    Simplified the logic for getting the graphid of entities
    where the entity already exists.
    
    Replaced the cypher_target_node field from transform entity
    with declared_in_current_clause.
    
    Renamed some AttrNumber in the cypher_target_node and
    cypher_create_path to reflect that they are AttrNumbers.
    
    Altered the id fields in cypher_target_node to simplify the
    logic.
    
    Added documentation to the transform_entity data structure.
    
    Added documentation to the cypher_target_node function.
    
    Altered the flow of logic when the CREATE clause must process
    all children tuples in one call of exec_cypher_create to be like
    the delete clause logic.
---
 src/backend/executor/cypher_create.c | 220 ++++++++++++++---------------
 src/backend/nodes/cypher_copyfuncs.c |  10 +-
 src/backend/nodes/cypher_outfuncs.c  |  10 +-
 src/backend/nodes/cypher_readfuncs.c |  10 +-
 src/backend/parser/cypher_clause.c   | 259 ++++++++++++++++-------------------
 src/include/nodes/cypher_nodes.h     |  72 +++++++---
 6 files changed, 299 insertions(+), 282 deletions(-)

diff --git a/src/backend/executor/cypher_create.c b/src/backend/executor/cypher_create.c
index a5769fe..c7aeb30 100644
--- a/src/backend/executor/cypher_create.c
+++ b/src/backend/executor/cypher_create.c
@@ -54,7 +54,6 @@ static Datum create_vertex(cypher_create_custom_scan_state *css,
 static HeapTuple insert_entity_tuple(ResultRelInfo *resultRelInfo,
                                 TupleTableSlot *elemTupleSlot, EState *estate);
 static void process_pattern(cypher_create_custom_scan_state *css);
-static void process_all_tuples(CustomScanState *node, EState *estate);
 static bool entity_exists(EState *estate, Oid graph_oid, graphid id);
 
 const CustomExecMethods cypher_create_exec_methods = {CREATE_SCAN_STATE_NAME,
@@ -104,7 +103,6 @@ static void begin_cypher_create(CustomScanState *node, EState *estate,
         {
             cypher_target_node *cypher_node =
                 (cypher_target_node *)lfirst(lc2);
-            ListCell *lc_expr;
             Relation rel;
 
             if (!CYPHER_TARGET_NODE_INSERT_ENTITY(cypher_node->flags))
@@ -114,7 +112,7 @@ static void begin_cypher_create(CustomScanState *node, EState *estate,
             rel = heap_open(cypher_node->relid, RowExclusiveLock);
 
             // Initialize resultRelInfo for the vertex
-            cypher_node->resultRelInfo = palloc(sizeof(ResultRelInfo));
+            cypher_node->resultRelInfo = makeNode(ResultRelInfo);
             InitResultRelInfo(cypher_node->resultRelInfo, rel,
                               list_length(estate->es_range_table), NULL,
                               estate->es_instrument);
@@ -127,14 +125,10 @@ static void begin_cypher_create(CustomScanState *node, EState *estate,
                 estate,
                 RelationGetDescr(cypher_node->resultRelInfo->ri_RelationDesc));
 
-            // setup expr states for the relation's target list
-            foreach (lc_expr, cypher_node->targetList)
+            if (cypher_node->id_expr != NULL)
             {
-                TargetEntry *te = lfirst(lc_expr);
-
-                cypher_node->expr_states =
-                    lappend(cypher_node->expr_states,
-                            ExecInitExpr(te->expr, (PlanState *)node));
+                cypher_node->id_expr_state =
+                    ExecInitExpr(cypher_node->id_expr, (PlanState *)node);
             }
         }
     }
@@ -179,7 +173,7 @@ static void process_pattern(cypher_create_custom_scan_state *css)
          * in the vertex/edge creation, create a path datum, and add to the
          * scantuple slot.
          */
-        if (path->tuple_position > 0)
+        if (path->path_attr_num != InvalidAttrNumber)
         {
             TupleTableSlot *scantuple;
             PlanState *ps;
@@ -190,83 +184,69 @@ static void process_pattern(cypher_create_custom_scan_state *css)
 
             result = make_path(css->path_values);
 
-            scantuple->tts_values[path->tuple_position - 1] = result;
-            scantuple->tts_isnull[path->tuple_position - 1] = false;
+            scantuple->tts_values[path->path_attr_num - 1] = result;
+            scantuple->tts_isnull[path->path_attr_num - 1] = false;
         }
 
         css->path_values = NIL;
     }
 }
 
-/*
- * When the CREATE clause is the last cypher clause, consume all input from the
- * previous clause(s) in the first call of exec_cypher_create.
- */
-static void process_all_tuples(CustomScanState *node, EState *estate)
-{
-    cypher_create_custom_scan_state *css =
-        (cypher_create_custom_scan_state *)node;
-    TupleTableSlot *slot;
-
-    do
-    {
-        process_pattern(css);
-
-        Decrement_Estate_CommandId(estate);
-        slot = ExecProcNode(node->ss.ps.lefttree);
-        Increment_Estate_CommandId(estate);
-    } while (!TupIsNull(slot));
-}
-
 static TupleTableSlot *exec_cypher_create(CustomScanState *node)
 {
     cypher_create_custom_scan_state *css =
         (cypher_create_custom_scan_state *)node;
-    ResultRelInfo *saved_resultRelInfo;
     EState *estate = css->css.ss.ps.state;
     ExprContext *econtext = css->css.ss.ps.ps_ExprContext;
     TupleTableSlot *slot;
-    MemoryContext old_mcxt;
-
-    saved_resultRelInfo = estate->es_result_relation_info;
-
-    //Process the subtree first
-    Decrement_Estate_CommandId(estate);
-    slot = ExecProcNode(node->ss.ps.lefttree);
-    Increment_Estate_CommandId(estate);
-
-    css->slot = slot;
-
-    econtext->ecxt_scantuple =
-        node->ss.ps.lefttree->ps_ProjInfo->pi_exprContext->ecxt_scantuple;
-
-    if (TupIsNull(slot))
-        return NULL;
-
-    old_mcxt = MemoryContextSwitchTo(econtext->ecxt_scantuple->tts_mcxt);
 
     if (CYPHER_CLAUSE_IS_TERMINAL(css->flags))
     {
-        process_all_tuples(node, estate);
+        /*
+         * If the CREATE clause was the final cypher clause written
+         * then we aren't returning anything from this result node.
+         * So the exec_cypher_create function will only be called once.
+         * Therefore we will process all tuples from the subtree at once.
+         */
+        while(true)
+        {
+            //Process the subtree first
+            Decrement_Estate_CommandId(estate)
+            slot = ExecProcNode(node->ss.ps.lefttree);
+            Increment_Estate_CommandId(estate)
+
+            if (TupIsNull(slot))
+                break;
 
-        MemoryContextSwitchTo(old_mcxt);
+            // setup the scantuple that the process_pattern needs
+            econtext->ecxt_scantuple =
+                node->ss.ps.lefttree->ps_ProjInfo->pi_exprContext->ecxt_scantuple;
 
-        estate->es_result_relation_info = saved_resultRelInfo;
+            css->tuple_info = NIL;
+
+            process_pattern(css);
+        }
 
         return NULL;
     }
     else
     {
-        process_pattern(css);
+        //Process the subtree first
+        Decrement_Estate_CommandId(estate)
+        slot = ExecProcNode(node->ss.ps.lefttree);
+        Increment_Estate_CommandId(estate)
 
-        MemoryContextSwitchTo(old_mcxt);
+        if (TupIsNull(slot))
+            return NULL;
 
-        estate->es_result_relation_info = saved_resultRelInfo;
+        // setup the scantuple that the process_delete_list needs
+        econtext->ecxt_scantuple =
+            node->ss.ps.lefttree->ps_ProjInfo->pi_exprContext->ecxt_scantuple;
+
+        css->tuple_info = NIL;
+
+        process_pattern(css);
 
-        /*
-         * Now that process_pattern has filled in the missing values,
-         * rerun the projection information.
-         */
         econtext->ecxt_scantuple =
             ExecProject(node->ss.ps.lefttree->ps_ProjInfo);
 
@@ -349,7 +329,6 @@ static void create_edge(cypher_create_custom_scan_state *css,
 {
     bool isNull;
     EState *estate = css->css.ss.ps.state;
-    ExprState *es;
     ExprContext *econtext = css->css.ss.ps.ps_ExprContext;
     ResultRelInfo *resultRelInfo = node->resultRelInfo;
     TupleTableSlot *elemTupleSlot = node->elemTupleSlot;
@@ -378,12 +357,18 @@ static void create_edge(cypher_create_custom_scan_state *css,
         start_id = prev_vertex_id;
         end_id = next_vertex_id;
     }
-    else
+    else if (node->dir == CYPHER_REL_DIR_LEFT)
     {
         // create pattern (prev_vertex)<-[edge]-(next_vertex)
         start_id = next_vertex_id;
         end_id = prev_vertex_id;
     }
+    else
+    {
+        ereport(ERROR,
+                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                 errmsg("edge direction must be specified in a CREATE clause")));
+    }
 
     /*
      * Set estate's result relation to the vertex's result
@@ -396,8 +381,7 @@ static void create_edge(cypher_create_custom_scan_state *css,
     ExecClearTuple(elemTupleSlot);
 
     // Graph Id for the edge
-    es = linitial(node->expr_states);
-    id = ExecEvalExpr(es, econtext, &isNull);
+    id = ExecEvalExpr(node->id_expr_state, econtext, &isNull);
     elemTupleSlot->tts_values[edge_tuple_id] = id;
     elemTupleSlot->tts_isnull[edge_tuple_id] = isNull;
 
@@ -411,9 +395,9 @@ static void create_edge(cypher_create_custom_scan_state *css,
 
     // Edge's properties map
     elemTupleSlot->tts_values[edge_tuple_properties] =
-        scanTupleSlot->tts_values[node->prop_var_no];
+        scanTupleSlot->tts_values[node->prop_attr_num];
     elemTupleSlot->tts_isnull[edge_tuple_properties] =
-        scanTupleSlot->tts_isnull[node->prop_var_no];
+        scanTupleSlot->tts_isnull[node->prop_attr_num];
 
     // Insert the new edge
     tuple = insert_entity_tuple(resultRelInfo, elemTupleSlot, estate);
@@ -435,7 +419,7 @@ static void create_edge(cypher_create_custom_scan_state *css,
 
         result = make_edge(
             id, start_id, end_id, CStringGetDatum(node->label_name),
-            PointerGetDatum(scanTupleSlot->tts_values[node->prop_var_no]));
+            PointerGetDatum(scanTupleSlot->tts_values[node->prop_attr_num]));
 
         if (CYPHER_TARGET_NODE_IN_PATH(node->flags))
         {
@@ -467,11 +451,13 @@ static Datum create_vertex(cypher_create_custom_scan_state *css,
 
     Assert(node->type == LABEL_KIND_VERTEX);
 
+    /*
+     * Vertices in a path might already exists. If they do get the id
+     * to pass to the edges before and after it. Otherwise, insert the
+     * new vertex into it's table and then pass the id along.
+     */
     if (CYPHER_TARGET_NODE_INSERT_ENTITY(node->flags))
     {
-        PlanState *ps = css->css.ss.ps.lefttree;
-        TupleTableSlot *scantuple = ps->ps_ExprContext->ecxt_scantuple;
-        ExprState *id_es = linitial(node->expr_states);
         HeapTuple tuple;
 
         /*
@@ -484,21 +470,26 @@ static Datum create_vertex(cypher_create_custom_scan_state *css,
 
         ExecClearTuple(elemTupleSlot);
 
-        id = ExecEvalExpr(id_es, econtext, &isNull);
+        // get the next graphid for this vertex.
+        id = ExecEvalExpr(node->id_expr_state, econtext, &isNull);
         elemTupleSlot->tts_values[vertex_tuple_id] = id;
         elemTupleSlot->tts_isnull[vertex_tuple_id] = isNull;
 
-        scantuple->tts_values[node->id_var_no - 1] = id;
-        scantuple->tts_isnull[node->id_var_no - 1] = isNull;
-
+        // get the properties for this vertex
         elemTupleSlot->tts_values[vertex_tuple_properties] =
-            scanTupleSlot->tts_values[node->prop_var_no];
+            scanTupleSlot->tts_values[node->prop_attr_num];
         elemTupleSlot->tts_isnull[vertex_tuple_properties] =
-            scanTupleSlot->tts_isnull[node->prop_var_no];
+            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;
@@ -526,13 +517,21 @@ static Datum create_vertex(cypher_create_custom_scan_state *css,
             ps = css->css.ss.ps.lefttree;
             scantuple = ps->ps_ExprContext->ecxt_scantuple;
 
+            // make the vertex agtype
             result = make_vertex(
                 id, CStringGetDatum(node->label_name),
-                PointerGetDatum(scanTupleSlot->tts_values[node->prop_var_no]));
+                PointerGetDatum(scanTupleSlot->tts_values[node->prop_attr_num]));
 
+            // append to the path list
             if (CYPHER_TARGET_NODE_IN_PATH(node->flags))
+            {
                 css->path_values = lappend(css->path_values, DatumGetPointer(result));
+            }
 
+            /*
+             * Put the vertex in the correct spot in the scantuple, so parent execution
+             * nodes can reference the newly created variable.
+             */
             if (CYPHER_TARGET_NODE_IS_VARIABLE(node->flags))
             {
                 scantuple->tts_values[node->tuple_position - 1] = result;
@@ -542,30 +541,46 @@ static Datum create_vertex(cypher_create_custom_scan_state *css,
     }
     else
     {
-        if (CYPHER_TARGET_NODE_ID_IS_AGTYPE(node->flags))
-        {
-            agtype *a;
-            agtype_value *v;
-            bool is_deleted = false;
+        agtype *a;
+        agtype_value *v;
+        agtype_value *id_value;
+        TupleTableSlot *scantuple;
+        PlanState *ps;
 
-            a = (agtype *)scanTupleSlot->tts_values[node->id_var_no];
+        ps = css->css.ss.ps.lefttree;
+        scantuple = ps->ps_ExprContext->ecxt_scantuple;
 
-            v = get_ith_agtype_value_from_container(&a->root, 0);
+        // get the vertex agtype in the scanTupleSlot
+        a = DATUM_GET_AGTYPE_P(scantuple->tts_values[node->tuple_position - 1]);
 
-            if (v->type != AGTV_INTEGER)
-                ereport(ERROR,
-                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                         errmsg("id agtype must resolve to a graphid")));
+        // Convert to an agtype value
+        v = get_ith_agtype_value_from_container(&a->root, 0);
 
-            id = GRAPHID_GET_DATUM(v->val.int_value);
+        if (v->type != AGTV_VERTEX)
+            ereport(ERROR,
+                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                     errmsg("agtype must resolve to a vertex")));
+
+        // extract the id agtype field
+        id_value = get_agtype_value_object_value(v, "id");
+
+        // extract the graphid and cast to a Datum
+        id = GRAPHID_GET_DATUM(id_value->val.int_value);
+
+        /*
+         * Its possible the variable has already been deleted. There are two ways
+         * this can happen. One is the query explicitly deleted the variable, the
+         * is_deleted flag will catch that. However, it is possible the user deleted
+         * the vertex using another variable name. We need to scan the table to find
+         * the vertex's current status relative to this CREATE clause. If the variable
+         * was initially created in this clause, we can skip this check, because the
+         * transaction system guarantees that nothing can happen to that tuple, as
+         * far as we are concerned with at this time.
+         */
+        if (!SAFE_TO_SKIP_EXISTENCE_CHECK(node->flags))
+        {
+            bool is_deleted = false;
 
-            /*
-             * Its possible the variable has already been deleted. There are two ways
-             * this can happen. One is the query explicitly deleted the variable, the
-             * is_deleted flag will catch that. However, it is possible the user deleted
-             * the vertex using another variable name. We need to scan the table to find
-             * the vertex's current status relative to this CREATE clause.
-             */
             get_heap_tuple(&css->css, node->variable_name, &is_deleted);
 
             if (is_deleted || !entity_exists(estate, css->graph_oid, DATUM_GET_GRAPHID(id)))
@@ -573,15 +588,6 @@ static Datum create_vertex(cypher_create_custom_scan_state *css,
                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                      errmsg("vertex assigned to variable %s was deleted", node->variable_name)));
         }
-        else if (CYPHER_TARGET_NODE_ID_IS_GRAPHID(node->flags))
-        {
-            id = scanTupleSlot->tts_values[node->id_var_no - 1];
-        }
-        else
-        {
-            ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                            errmsg("id agtype must resolve to a graphid")));
-        }
 
         if (CYPHER_TARGET_NODE_IN_PATH(node->flags))
         {
diff --git a/src/backend/nodes/cypher_copyfuncs.c b/src/backend/nodes/cypher_copyfuncs.c
index 59632ac..e1546e9 100644
--- a/src/backend/nodes/cypher_copyfuncs.c
+++ b/src/backend/nodes/cypher_copyfuncs.c
@@ -87,7 +87,7 @@ void copy_cypher_create_path(ExtensibleNode *newnode, const ExtensibleNode *from
 {
     COPY_LOCALS(cypher_create_path);
 
-    COPY_SCALAR_FIELD(tuple_position);
+    COPY_SCALAR_FIELD(path_attr_num);
 
     COPY_NODE_FIELD(target_nodes);
 }
@@ -100,19 +100,17 @@ void copy_cypher_target_node(ExtensibleNode *newnode, const ExtensibleNode *from
     COPY_SCALAR_FIELD(type);
     COPY_SCALAR_FIELD(flags);
     COPY_SCALAR_FIELD(dir);
-    COPY_SCALAR_FIELD(id_var_no);
-    COPY_SCALAR_FIELD(prop_var_no);
+    COPY_SCALAR_FIELD(prop_attr_num);
     COPY_SCALAR_FIELD(relid);
     COPY_SCALAR_FIELD(tuple_position);
 
     COPY_STRING_FIELD(label_name);
     COPY_STRING_FIELD(variable_name);
 
+    COPY_NODE_FIELD(id_expr);
+    COPY_NODE_FIELD(id_expr_state);
     COPY_NODE_FIELD(resultRelInfo);
     COPY_NODE_FIELD(elemTupleSlot);
-    COPY_NODE_FIELD(te);
-    COPY_NODE_FIELD(targetList);
-    COPY_NODE_FIELD(expr_states);
 }
 
 // copy function for cypher_update_information
diff --git a/src/backend/nodes/cypher_outfuncs.c b/src/backend/nodes/cypher_outfuncs.c
index d0ef9e8..fb5abd8 100644
--- a/src/backend/nodes/cypher_outfuncs.c
+++ b/src/backend/nodes/cypher_outfuncs.c
@@ -291,7 +291,7 @@ void out_cypher_create_path(StringInfo str, const ExtensibleNode *node)
     DEFINE_AG_NODE(cypher_create_path);
 
     WRITE_NODE_FIELD(target_nodes);
-    WRITE_INT32_FIELD(tuple_position);
+    WRITE_INT32_FIELD(path_attr_num);
 }
 
 // serialization function for the cypher_target_node ExtensibleNode.
@@ -302,11 +302,9 @@ void out_cypher_target_node(StringInfo str, const ExtensibleNode *node)
     WRITE_CHAR_FIELD(type);
     WRITE_INT32_FIELD(flags);
     WRITE_ENUM_FIELD(dir, cypher_rel_dir);
-    WRITE_INT32_FIELD(id_var_no);
-    WRITE_INT32_FIELD(prop_var_no);
-    WRITE_NODE_FIELD(targetList);
-    WRITE_NODE_FIELD(te);
-    WRITE_NODE_FIELD(expr_states);
+    WRITE_NODE_FIELD(id_expr);
+    WRITE_NODE_FIELD(id_expr_state);
+    WRITE_INT32_FIELD(prop_attr_num);
     WRITE_NODE_FIELD(resultRelInfo);
     WRITE_NODE_FIELD(elemTupleSlot);
     WRITE_OID_FIELD(relid);
diff --git a/src/backend/nodes/cypher_readfuncs.c b/src/backend/nodes/cypher_readfuncs.c
index 5486c69..76fe63b 100644
--- a/src/backend/nodes/cypher_readfuncs.c
+++ b/src/backend/nodes/cypher_readfuncs.c
@@ -191,7 +191,7 @@ void read_cypher_create_path(struct ExtensibleNode *node)
     READ_LOCALS(cypher_create_path);
 
     READ_NODE_FIELD(target_nodes);
-    READ_INT_FIELD(tuple_position);
+    READ_INT_FIELD(path_attr_num);
 }
 
 /*
@@ -205,11 +205,9 @@ void read_cypher_target_node(struct ExtensibleNode *node)
     READ_CHAR_FIELD(type);
     READ_INT_FIELD(flags);
     READ_ENUM_FIELD(dir, cypher_rel_dir);
-    READ_INT_FIELD(id_var_no);
-    READ_INT_FIELD(prop_var_no);
-    READ_NODE_FIELD(targetList);
-    READ_NODE_FIELD(te);
-    READ_NODE_FIELD(expr_states);
+    READ_NODE_FIELD(id_expr);
+    READ_NODE_FIELD(id_expr_state);
+    READ_INT_FIELD(prop_attr_num);
     READ_NODE_FIELD(resultRelInfo);
     READ_NODE_FIELD(elemTupleSlot);
     READ_OID_FIELD(relid);
diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c
index 57d9e05..8b9ebac 100644
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@ -90,12 +90,46 @@ enum transform_entity_join_side
     JOIN_SIDE_RIGHT
 };
 
+/*
+ * In the transformation stage, we need to track
+ * where a variable came from. When moving between
+ * clauses, Postgres parsestate and Query data structures
+ * are insufficient for some of the information we
+ * need.
+ */
 typedef struct
 {
+    // denotes whether this entity is a vertex or edge
     enum transform_entity_type type;
+
+    /*
+     * MATCH clauses are transformed into a select * FROM ... JOIN, etc
+     * We need to know wheter the table that this entity represents is
+     * part of the join tree. If a cypher_node does not meet the conditions
+     * set in INCLUDE_NODE_IN_JOIN_TREE. Then we can skip the node when
+     * constructing our join tree. The entities around this particular entity
+     * need to know this for the join to get properly constructed.
+     */
     bool in_join_tree;
+
+    /*
+     * The parse data structure will be transformed into an Expr that represents
+     * the entity. When contructing the join tree, we need to know what it was
+     * turned into. If the entity was originally created in a previous clause,
+     * this will be a Var that we need to reference to extract the id, startid,
+     * endid for the join. If the entity was created in the current clause, then
+     * this will be a FuncExpr that we can reference to get the id, startid, and
+     * endid.
+     */
     Expr *expr;
-    cypher_target_node *target_node;
+
+    /*
+     * tells each clause whether this variable was
+     * declared by itself or a previous clause.
+     */
+    bool declared_in_current_clause;
+
+    // The parse data structure that we transformed
     union
     {
         cypher_node *node;
@@ -181,8 +215,7 @@ static A_Expr *filter_vertices_on_label_id(cypher_parsestate *cpstate,
                                            FuncCall *id_field, char *label);
 static transform_entity *
 make_transform_entity(cypher_parsestate *cpstate,
-                      enum transform_entity_type type, Node *node, Expr *expr,
-                      cypher_target_node *target_node);
+                      enum transform_entity_type type, Node *node, Expr *expr);
 static transform_entity *find_variable(cypher_parsestate *cpstate, char *name);
 static Node *create_property_constraint_function(cypher_parsestate *cpstate,
                                                  transform_entity *entity,
@@ -203,18 +236,11 @@ static cypher_target_node *
 transform_create_cypher_new_node(cypher_parsestate *cpstate,
                                  List **target_list, cypher_node *node);
 static cypher_target_node *transform_create_cypher_existing_node(
-    cypher_parsestate *cpstate, List **target_list, transform_entity *entity,
+    cypher_parsestate *cpstate, List **target_list, bool declared_in_current_clause,
     cypher_node *node);
 static cypher_target_node *
 transform_create_cypher_edge(cypher_parsestate *cpstate, List **target_list,
                              cypher_relationship *edge);
-static Expr *cypher_create_id_access_function(cypher_parsestate *cpstate,
-                                              RangeTblEntry *rte,
-                                              enum transform_entity_type type,
-                                              char *name);
-static Node *cypher_create_id_default(cypher_parsestate *cpstate,
-                                      Relation label_relation,
-                                      enum transform_entity_type type);
 static Expr *cypher_create_properties(cypher_parsestate *cpstate,
                                       cypher_target_node *rel,
                                       Relation label_relation, Node *props,
@@ -274,6 +300,7 @@ static Index transform_group_clause_expr(List **flatresult,
 static List *add_target_to_group_list(cypher_parsestate *cpstate,
                                       TargetEntry *tle, List *grouplist,
                                       List *targetlist, int location);
+static void advance_transform_entities_to_next_clause(List *entities);
 /*
  * transform a cypher_clause
  */
@@ -1682,8 +1709,7 @@ static A_Expr *filter_vertices_on_label_id(cypher_parsestate *cpstate,
 
 static transform_entity *make_transform_entity(cypher_parsestate *cpstate,
                                                enum transform_entity_type type,
-                                               Node *node, Expr *expr,
-                                               cypher_target_node *target_node)
+                                               Node *node, Expr *expr)
 {
     transform_entity *entity;
 
@@ -1698,11 +1724,10 @@ static transform_entity *make_transform_entity(cypher_parsestate *cpstate,
         ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                         errmsg("unknown entity type")));
 
-    entity->target_node = target_node;
+    entity->declared_in_current_clause = true;
     entity->expr = expr;
     entity->in_join_tree = expr != NULL;
 
-    cpstate->entities = lappend(cpstate->entities, entity);
     return entity;
 }
 
@@ -1850,7 +1875,9 @@ static List *transform_match_entities(cypher_parsestate *cpstate, Query *query,
                                       INCLUDE_NODE_IN_JOIN_TREE(path, node));
 
             entity = make_transform_entity(cpstate, ENT_VERTEX, (Node *)node,
-                                           expr, NULL);
+                                           expr);
+
+            cpstate->entities = lappend(cpstate->entities, entity);
 
             if (node->props)
             {
@@ -1867,7 +1894,9 @@ static List *transform_match_entities(cypher_parsestate *cpstate, Query *query,
             expr = transform_cypher_edge(cpstate, rel, &query->targetList);
 
             entity = make_transform_entity(cpstate, ENT_EDGE, (Node *)rel,
-                                           expr, NULL);
+                                           expr);
+
+            cpstate->entities = lappend(cpstate->entities, entity);
 
             if (rel->props)
             {
@@ -1997,9 +2026,6 @@ transform_match_create_path_variable(cypher_parsestate *cpstate,
  * Maps a column name to the a function access name. In others word when
  * passed the name for the vertex's id column name, return the function name
  * for the vertex's agtype id element, etc.
- *
- * Note: the property colname is not here, because an access function does not
- * currently exist and is not needed.
  */
 static char *get_accessor_function_name(enum transform_entity_type type,
                                         char *name)
@@ -2525,6 +2551,8 @@ transform_cypher_create_path(cypher_parsestate *cpstate, List **target_list,
     cypher_create_path *ccp = make_ag_node(cypher_create_path);
     bool in_path = path->var_name != NULL;
 
+    ccp->path_attr_num = InvalidAttrNumber;
+
     foreach (lc, path->path)
     {
         if (is_ag_node(lfirst(lc), cypher_node))
@@ -2541,7 +2569,7 @@ transform_cypher_create_path(cypher_parsestate *cpstate, List **target_list,
             transformed_path = lappend(transformed_path, rel);
 
             entity = make_transform_entity(cpstate, ENT_VERTEX, (Node *)node,
-                                           NULL, rel);
+                                           NULL);
 
             cpstate->entities = lappend(cpstate->entities, entity);
         }
@@ -2559,7 +2587,7 @@ transform_cypher_create_path(cypher_parsestate *cpstate, List **target_list,
             transformed_path = lappend(transformed_path, rel);
 
             entity = make_transform_entity(cpstate, ENT_EDGE, (Node *)edge,
-                                           NULL, rel);
+                                           NULL);
 
             cpstate->entities = lappend(cpstate->entities, entity);
         }
@@ -2572,28 +2600,30 @@ transform_cypher_create_path(cypher_parsestate *cpstate, List **target_list,
 
     ccp->target_nodes = transformed_path;
 
+    /*
+     * If this path a variable, create a placeholder entry that we can fill
+     * in with during the execution phase.
+     */
     if (path->var_name)
     {
         TargetEntry *te;
 
         if (list_length(transformed_path) < 3)
+        {
             ereport(
                 ERROR,
                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                  errmsg("paths consist of alternating vertices and edges."),
                  parser_errposition(pstate, path->location),
                  errhint("paths require at least 2 vertices and 1 edge")));
+        }
 
         te = placeholder_target_entry(cpstate, path->var_name);
 
-        ccp->tuple_position = te->resno;
+        ccp->path_attr_num = te->resno;
 
         *target_list = lappend(*target_list, te);
     }
-    else
-    {
-        ccp->tuple_position = -1;
-    }
 
     return ccp;
 }
@@ -2604,18 +2634,18 @@ transform_create_cypher_edge(cypher_parsestate *cpstate, List **target_list,
 {
     ParseState *pstate = (ParseState *)cpstate;
     cypher_target_node *rel = make_ag_node(cypher_target_node);
-    List *targetList = NIL;
-    Expr *id, *props;
+    Expr *props;
     Relation label_relation;
     RangeVar *rv;
     RangeTblEntry *rte;
     TargetEntry *te;
     char *alias;
-    int resno;
+    AttrNumber resno;
 
     rel->type = LABEL_KIND_EDGE;
     rel->flags = CYPHER_TARGET_NODE_FLAG_INSERT;
     rel->label_name = edge->label;
+    rel->resultRelInfo = NULL;
 
     if (edge->name)
     {
@@ -2681,14 +2711,9 @@ transform_create_cypher_edge(cypher_parsestate *cpstate, List **target_list,
     rte->requiredPerms = ACL_INSERT;
 
     // Build Id expression, always use the default logic
-    id = (Expr *)build_column_default(label_relation,
+    rel->id_expr = (Expr *)build_column_default(label_relation,
                                       Anum_ag_label_edge_table_id);
 
-    te = makeTargetEntry(id, InvalidAttrNumber, AGE_VARNAME_ID, false);
-    targetList = lappend(targetList, te);
-
-    rel->targetList = targetList;
-
     // Build properties expression, if no map is given, use the default logic
     alias = get_next_default_alias(cpstate);
     resno = pstate->p_next_resno++;
@@ -2696,7 +2721,7 @@ transform_create_cypher_edge(cypher_parsestate *cpstate, List **target_list,
     props = cypher_create_properties(cpstate, rel, label_relation, edge->props,
                                      ENT_EDGE);
 
-    rel->prop_var_no = resno - 1;
+    rel->prop_attr_num = resno - 1;
     te = makeTargetEntry(props, resno, alias, false);
 
     *target_list = lappend(*target_list, te);
@@ -2704,8 +2729,6 @@ transform_create_cypher_edge(cypher_parsestate *cpstate, List **target_list,
     // Keep the lock
     heap_close(label_relation, NoLock);
 
-    rel->expr_states = NIL;
-
     return rel;
 }
 
@@ -2734,6 +2757,8 @@ static cypher_target_node *
 transform_create_cypher_node(cypher_parsestate *cpstate, List **target_list,
                              cypher_node *node)
 {
+    ParseState *pstate = (ParseState *)cpstate;
+
     /*
      *  Check if the variable already exists, if so find the entity and
      *  setup the target node
@@ -2746,8 +2771,13 @@ transform_create_cypher_node(cypher_parsestate *cpstate, List **target_list,
 
         if (entity)
         {
+            if (entity->type != ENT_VERTEX)
+                ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("variable %s already exists", node->name),
+                                parser_errposition(pstate, node->location)));
+
             return transform_create_cypher_existing_node(cpstate, target_list,
-                                                         entity, node);
+                                                         entity->declared_in_current_clause, node);
         }
     }
 
@@ -2755,25 +2785,6 @@ transform_create_cypher_node(cypher_parsestate *cpstate, List **target_list,
     return transform_create_cypher_new_node(cpstate, target_list, node);
 }
 
-static Expr *cypher_create_id_access_function(cypher_parsestate *cpstate,
-                                              RangeTblEntry *rte,
-                                              enum transform_entity_type type,
-                                              char *name)
-{
-    ParseState *pstate = (ParseState *)cpstate;
-    Node *col;
-    Oid func_oid;
-    FuncExpr *func_expr;
-    col = scanRTEForColumn(pstate, rte, name, -1, 0, NULL);
-
-    func_oid = get_ag_func_oid(AG_ACCESS_FUNCTION_ID, 1, AGTYPEOID);
-
-    func_expr = makeFuncExpr(func_oid, AGTYPEOID, list_make1(col), InvalidOid,
-                             InvalidOid, COERCE_EXPLICIT_CALL);
-
-    return add_volatile_wrapper((Expr *)func_expr);
-}
-
 /*
  * Returns the resno for the TargetEntry with the resname equal to the name
  * passed. Returns -1 otherwise.
@@ -2797,31 +2808,20 @@ static int get_target_entry_resno(List *target_list, char *name)
 
 /*
  * Transform logic for a previously declared variable in a CREATE clause.
- * All we need from the variable node is its id. Add an access to the
- * id and mark where in the target list we can find the id value.
+ * All we need from the variable node is its id, and whether we can skip
+ * some tests in the execution phase..
  */
 static cypher_target_node *transform_create_cypher_existing_node(
-    cypher_parsestate *cpstate, List **target_list, transform_entity *entity,
+    cypher_parsestate *cpstate, List **target_list, bool declared_in_current_clause,
     cypher_node *node)
 {
-    ParseState *pstate = (ParseState *)cpstate;
     cypher_target_node *rel = make_ag_node(cypher_target_node);
-    char *alias;
-    int resno;
-    RangeTblEntry *rte = find_rte(cpstate, PREV_CYPHER_CLAUSE_ALIAS);
-    TargetEntry *te;
-    Expr *result;
 
     rel->type = LABEL_KIND_VERTEX;
     rel->flags = CYPHER_TARGET_NODE_FLAG_NONE;
-
+    rel->resultRelInfo = NULL;
     rel->variable_name = node->name;
-    rel->tuple_position = 0;
 
-    if (entity->type != ENT_VERTEX)
-        ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                        errmsg("variable %s already exists", node->name),
-                        parser_errposition(pstate, node->location)));
 
     if (node->props)
         ereport(
@@ -2835,39 +2835,19 @@ static cypher_target_node *transform_create_cypher_existing_node(
                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                  errmsg("previously declared variables cannot have a label")));
 
-    if (entity->target_node)
+    /*
+     * When the variable is declared in the same clause this vertex is a part of
+     * we can skip some expensive checks in the execution phase.
+     */
+    if (declared_in_current_clause)
     {
-        cypher_target_node *prev_target_node = entity->target_node;
-
-        rel->flags |= CYPHER_TARGET_NODE_CUR_VAR;
-        rel->id_var_no = prev_target_node->id_var_no;
-        rel->tuple_position = prev_target_node->tuple_position;
-
-        return rel;
+        rel->flags |= EXISTING_VARAIBLE_DECLARED_SAME_CLAUSE;
     }
-    else
-    {
-        rel->flags |= CYPHER_TARGET_NODE_PREV_VAR;
-
-        alias = get_next_default_alias(cpstate);
-        resno = pstate->p_next_resno++;
-
-        rel->relid = -1;
-
-        // id
-        result = cypher_create_id_access_function(cpstate, rte, ENT_VERTEX,
-                                                  node->name);
-
-        rel->id_var_no = resno - 1;
-        te = makeTargetEntry(result, resno, alias, false);
-        *target_list = lappend(*target_list, te);
 
-        rel->tuple_position = get_target_entry_resno(*target_list, node->name);
+    // get the AttrNumber the variable is stored in, so we can extract the id later.
+    rel->tuple_position = get_target_entry_resno(*target_list, node->name);
 
-        rel->expr_states = NIL;
-
-        return rel;
-    }
+    return rel;
 }
 
 /*
@@ -2880,7 +2860,6 @@ transform_create_cypher_new_node(cypher_parsestate *cpstate,
 {
     ParseState *pstate = (ParseState *)cpstate;
     cypher_target_node *rel = make_ag_node(cypher_target_node);
-    Node *id;
     Relation label_relation;
     RangeVar *rv;
     RangeTblEntry *rte;
@@ -2890,10 +2869,17 @@ transform_create_cypher_new_node(cypher_parsestate *cpstate,
     int resno;
 
     rel->type = LABEL_KIND_VERTEX;
+    rel->tuple_position = InvalidAttrNumber;
+    rel->variable_name = NULL;
+    rel->resultRelInfo = NULL;
 
     if (!node->label)
     {
         rel->label_name = "";
+        /*
+         *  If no label is specified, assign the generic label name that
+         *  all labels are descendents of.
+         */
         node->label = AG_DEFAULT_LABEL_VERTEX;
     }
     else
@@ -2929,16 +2915,8 @@ transform_create_cypher_new_node(cypher_parsestate *cpstate,
     rte->requiredPerms = ACL_INSERT;
 
     // id
-    id = cypher_create_id_default(cpstate, label_relation, ENT_VERTEX);
-
-    te = makeTargetEntry((Expr *)id, InvalidAttrNumber, AGE_VARNAME_ID, false);
-    rel->targetList = list_make1(te);
-    rel->id_var_no = -1;
-
-    alias = get_next_default_alias(cpstate);
-    te = placeholder_target_entry(cpstate, alias);
-    *target_list = lappend(*target_list, te);
-    rel->id_var_no = te->resno;
+    rel->id_expr = (Expr *)build_column_default(label_relation,
+                                                Anum_ag_label_vertex_table_id);
 
     // properties
     alias = get_next_default_alias(cpstate);
@@ -2947,14 +2925,12 @@ transform_create_cypher_new_node(cypher_parsestate *cpstate,
     props = cypher_create_properties(cpstate, rel, label_relation, node->props,
                                      ENT_VERTEX);
 
-    rel->prop_var_no = resno - 1;
+    rel->prop_attr_num = resno - 1;
     te = makeTargetEntry(props, resno, alias, false);
     *target_list = lappend(*target_list, te);
 
     heap_close(label_relation, NoLock);
 
-    rel->expr_states = NIL;
-
     if (node->name)
     {
         rel->variable_name = node->name;
@@ -2963,11 +2939,6 @@ transform_create_cypher_new_node(cypher_parsestate *cpstate,
         *target_list = lappend(*target_list, te);
         rel->flags |= CYPHER_TARGET_NODE_IS_VAR;
     }
-    else
-    {
-        rel->variable_name = NULL;
-        rel->tuple_position = 0;
-    }
 
     return rel;
 }
@@ -3016,38 +2987,29 @@ static Expr *cypher_create_properties(cypher_parsestate *cpstate,
     }
 
     if (props)
+    {
         properties = (Expr *)transform_cypher_expr(cpstate, props,
                                                    EXPR_KIND_INSERT_TARGET);
+    }
     else if (type == ENT_VERTEX)
+    {
         properties = (Expr *)build_column_default(
             label_relation, Anum_ag_label_vertex_table_properties);
+    }
     else if (type == ENT_EDGE)
+    {
         properties = (Expr *)build_column_default(
             label_relation, Anum_ag_label_edge_table_properties);
+    }
     else
+    {
 	ereport(ERROR, (errmsg_internal("unreconized entity type")));
+    }
 
     // add a volatile wrapper call to prevent the optimizer from removing it
     return (Expr *)add_volatile_wrapper(properties);
 }
 
-static Node *cypher_create_id_default(cypher_parsestate *cpstate,
-                                      Relation label_relation,
-                                      enum transform_entity_type type)
-{
-    Node *id = NULL;
-
-    if (type == ENT_VERTEX)
-        id = build_column_default(label_relation,
-                                  Anum_ag_label_vertex_table_id);
-    else if (type == ENT_EDGE)
-        id = build_column_default(label_relation, Anum_ag_label_edge_table_id);
-    else
-        ereport(ERROR, (errmsg_internal("unreconized entity type")));
-
-    return id;
-}
-
 /*
  * makeNamespaceItem (from PG makeNamespaceItem)-
  *        Convenience subroutine to construct a ParseNamespaceItem.
@@ -3142,6 +3104,23 @@ transform_cypher_clause_as_subquery(cypher_parsestate *cpstate,
     return rte;
 }
 
+/*
+ * When we are done transforming a clause, before transforming the next clause
+ * iterate through the transform entities and mark them as not belonging to
+ * the clause that is currently being transformed.
+ */
+static void advance_transform_entities_to_next_clause(List *entities)
+{
+    ListCell *lc;
+
+    foreach (lc, entities)
+    {
+        transform_entity *entity = lfirst(lc);
+
+        entity->declared_in_current_clause = false;
+    }
+}
+
 static Query *analyze_cypher_clause(transform_method transform,
                                     cypher_clause *clause,
                                     cypher_parsestate *parent_cpstate)
@@ -3159,6 +3138,8 @@ static Query *analyze_cypher_clause(transform_method transform,
 
     query = transform(cpstate, clause);
 
+    advance_transform_entities_to_next_clause(cpstate->entities);
+
     parent_cpstate->entities = list_concat(parent_cpstate->entities,
                                            cpstate->entities);
     free_cypher_parsestate(cpstate);
diff --git a/src/include/nodes/cypher_nodes.h b/src/include/nodes/cypher_nodes.h
index 6deaa77..5cbd036 100644
--- a/src/include/nodes/cypher_nodes.h
+++ b/src/include/nodes/cypher_nodes.h
@@ -214,7 +214,7 @@ typedef struct cypher_create_path
 {
     ExtensibleNode extensible;
     List *target_nodes;
-    AttrNumber tuple_position;
+    AttrNumber path_attr_num;
 } cypher_create_path;
 
 #define CYPHER_CLAUSE_FLAG_NONE 0x0000
@@ -227,36 +227,72 @@ typedef struct cypher_create_path
 #define CYPHER_CLAUSE_HAS_PREVIOUS_CLAUSE(flags) \
     (flags & CYPHER_CLAUSE_FLAG_PREVIOUS_CLAUSE)
 
+/*
+ * Structure that contains all information to create
+ * a new entity in the create clause, or where to access
+ * this information if it doesn't need to be created.
+ *
+ * NOTE: This structure may be used for the MERGE clause as
+ * well
+ */
 typedef struct cypher_target_node
 {
     ExtensibleNode extensible;
+    // 'v' for vertex or 'e' for edge
     char type;
+    // flags defined below, prefaced with CYPHER_TARGET_NODE_FLAG_*
     uint32 flags;
+    // if an edge, denotes direction
     cypher_rel_dir dir;
-    int id_var_no;
-    int prop_var_no;
-    List *targetList;
-    TargetEntry *te;
-    List *expr_states;
+    /*
+     * Used to create the id for the vertex/edge,
+     * if the CYPHER_TARGET_NODE_FLAG_INSERT flag
+     * is set. Doing it this way will protect us when
+     * rescan gets implemented. By calling the function
+     * that creates the id ourselves, we won't have an
+     * issue where the id could be created then not used.
+     * Since there is a limited number of ids available, we
+     * don't want to waste them.
+     */
+    Expr *id_expr;
+    ExprState *id_expr_state;
+    /*
+     * Attribute Number that this entity's properties
+     * are stored in the CustomScanState's child TupleTableSlot
+     */
+    AttrNumber prop_attr_num;
+    // RelInfo for the table this entity will be stored in
     ResultRelInfo *resultRelInfo;
+    // elemTupleSlot used to insert the entity into its table
     TupleTableSlot *elemTupleSlot;
+    // relid that the label stores its entity
     Oid relid;
+    // label this entity belongs to.
     char *label_name;
+    // variable name for this entity
     char *variable_name;
+    /*
+     * Attribute number this entity needs to be stored in
+     * for parent execution nodes to reference it. If the
+     * entity is a varaible (CYPHER_TARGET_NODE_IS_VAR).
+     */
     AttrNumber tuple_position;
 } cypher_target_node;
 
 #define CYPHER_TARGET_NODE_FLAG_NONE 0x0000
 // node must insert data
 #define CYPHER_TARGET_NODE_FLAG_INSERT 0x0001
-//node is a var declared in a previous clause
-#define CYPHER_TARGET_NODE_PREV_VAR 0x0002
-//node is a var declared in the current clause
-#define CYPHER_TARGET_NODE_CUR_VAR 0x0004
+/*
+ * Flag that denotes if this target node is referencing
+ * a variable that was already created AND created in the
+ * same clause.
+ */
+#define EXISTING_VARAIBLE_DECLARED_SAME_CLAUSE 0x0002
+
 //node is the first instance of a declared variable
-#define CYPHER_TARGET_NODE_IS_VAR 0x0008
+#define CYPHER_TARGET_NODE_IS_VAR 0x0004
 // node is an element in a path variable
-#define CYPHER_TARGET_NODE_IN_PATH_VAR 0x0010
+#define CYPHER_TARGET_NODE_IN_PATH_VAR 0x0008
 
 #define CYPHER_TARGET_NODE_OUTPUT(flags) \
     (flags & (CYPHER_TARGET_NODE_IS_VAR | CYPHER_TARGET_NODE_IN_PATH_VAR))
@@ -267,12 +303,12 @@ typedef struct cypher_target_node
 #define CYPHER_TARGET_NODE_IS_VARIABLE(flags) \
     (flags & CYPHER_TARGET_NODE_IS_VAR)
 
-#define CYPHER_TARGET_NODE_ID_IS_GRAPHID(flags) \
-    (flags & CYPHER_TARGET_NODE_CUR_VAR)
-
-#define CYPHER_TARGET_NODE_ID_IS_AGTYPE(flags) \
-    (flags & CYPHER_TARGET_NODE_PREV_VAR)
-
+/*
+ * When a vertex is created and is reference in the same clause
+ * later. We don't need to check to see if the vertex still exists.
+ */
+#define SAFE_TO_SKIP_EXISTENCE_CHECK(flags) \
+    (flags & EXISTING_VARAIBLE_DECLARED_SAME_CLAUSE)
 
 #define CYPHER_TARGET_NODE_INSERT_ENTITY(flags) \
     (flags & CYPHER_TARGET_NODE_FLAG_INSERT)