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