3232#include "parser/parsetree.h"
3333#include "storage/bufmgr.h"
3434#include "utils/rel.h"
35+ #include "common/hashfn.h"
3536
3637#include "catalog/ag_label.h"
3738#include "executor/cypher_executor.h"
@@ -48,9 +49,7 @@ static void rescan_cypher_delete(CustomScanState *node);
4849
4950static void process_delete_list (CustomScanState * node );
5051
51- static void find_connected_edges (CustomScanState * node , char * graph_name ,
52- List * labels , char * var_name , graphid id ,
53- bool detach_delete );
52+ static void check_for_connected_edges (CustomScanState * node );
5453static agtype_value * extract_entity (CustomScanState * node ,
5554 TupleTableSlot * scanTupleSlot ,
5655 int entity_position );
@@ -83,6 +82,7 @@ static void begin_cypher_delete(CustomScanState *node, EState *estate,
8382 cypher_delete_custom_scan_state * css =
8483 (cypher_delete_custom_scan_state * )node ;
8584 Plan * subplan ;
85+ HASHCTL hashctl ;
8686
8787 Assert (list_length (css -> cs -> custom_plans ) == 1 );
8888
@@ -112,6 +112,16 @@ static void begin_cypher_delete(CustomScanState *node, EState *estate,
112112 */
113113 css -> edge_labels = get_all_edge_labels_per_graph (estate , css -> delete_data -> graph_oid );
114114
115+ /* init vertex_id_htab */
116+ MemSet (& hashctl , 0 , sizeof (hashctl ));
117+ hashctl .keysize = sizeof (graphid );
118+ hashctl .entrysize =
119+ sizeof (graphid ); // entries are not used, but entrysize must >= keysize
120+ hashctl .hash = tag_hash ;
121+ css -> vertex_id_htab = hash_create (DELETE_VERTEX_HTAB_NAME ,
122+ DELETE_VERTEX_HTAB_SIZE , & hashctl ,
123+ HASH_ELEM | HASH_FUNCTION );
124+
115125 /*
116126 * Postgres does not assign the es_output_cid in queries that do
117127 * not write to disk, ie: SELECT commands. We need the command id
@@ -194,6 +204,10 @@ static TupleTableSlot *exec_cypher_delete(CustomScanState *node)
194204 */
195205static void end_cypher_delete (CustomScanState * node )
196206{
207+ check_for_connected_edges (node );
208+
209+ hash_destroy (((cypher_delete_custom_scan_state * )node )-> vertex_id_htab );
210+
197211 ExecEndNode (node -> ss .ps .lefttree );
198212}
199213
@@ -443,15 +457,15 @@ static void process_delete_list(CustomScanState *node)
443457 }
444458
445459 /*
446- * For vertices, we need to check if the vertex is connected to any
447- * edges, * if there are, we need to delete them or throw an error,
448- * depending on if the query specified the DETACH option .
460+ * For vertices, we insert the vertex ID in the hashtable
461+ * vertex_id_htab. This hashtable is used later to process
462+ * connected edges .
449463 */
450464 if (original_entity_value -> type == AGTV_VERTEX )
451465 {
452- find_connected_edges ( node , css -> delete_data -> graph_name ,
453- css -> edge_labels , item -> var_name ,
454- id -> val . int_value , css -> delete_data -> detach );
466+ bool found ;
467+ hash_search ( css -> vertex_id_htab , ( void * ) & ( id -> val . int_value ) ,
468+ HASH_ENTER , & found );
455469 }
456470
457471 /* At this point, we are ready to delete the node/vertex. */
@@ -464,85 +478,87 @@ static void process_delete_list(CustomScanState *node)
464478}
465479
466480/*
467- * Find the edges connected to the given node. If there is any edges either
468- * delete them or throw an error, depending on the detach delete option.
481+ * Scans the edge tables and checks if the deleted vertices are connected to
482+ * any edge(s). For DETACH DELETE, the connected edges are deleted. Otherwise,
483+ * an error is thrown.
469484 */
470- static void find_connected_edges (CustomScanState * node , char * graph_name ,
471- List * labels , char * var_name , graphid id ,
472- bool detach_delete )
485+ static void check_for_connected_edges (CustomScanState * node )
473486{
487+ ListCell * lc ;
474488 cypher_delete_custom_scan_state * css =
475489 (cypher_delete_custom_scan_state * )node ;
476490 EState * estate = css -> css .ss .ps .state ;
477- ListCell * lc ;
491+ char * graph_name = css -> delete_data -> graph_name ;
478492
479- Increment_Estate_CommandId (estate );
480-
481- /*
482- * We need to scan through all the edges to see if this vertex has
483- * any edges attached to it.
484- *
485- * XXX: If we implement an on-disc graph storage system. Such as
486- * an adjacency matrix, the performance of this check can be massively
487- * improved. However, right now we have to scan every edge to see if
488- * one has this vertex as a start or end vertex.
489- */
490- foreach (lc , labels )
493+ /* scans each label from css->edge_labels */
494+ foreach (lc , css -> edge_labels )
491495 {
492496 char * label_name = lfirst (lc );
493497 ResultRelInfo * resultRelInfo ;
494498 TableScanDesc scan_desc ;
495499 HeapTuple tuple ;
496500 TupleTableSlot * slot ;
497501
498- resultRelInfo = create_entity_result_rel_info (estate ,
499- graph_name , label_name );
500-
502+ resultRelInfo = create_entity_result_rel_info (estate , graph_name ,
503+ label_name );
501504 scan_desc = table_beginscan (resultRelInfo -> ri_RelationDesc ,
502505 estate -> es_snapshot , 0 , NULL );
503-
504506 slot = ExecInitExtraTupleSlot (
505507 estate , RelationGetDescr (resultRelInfo -> ri_RelationDesc ),
506508 & TTSOpsHeapTuple );
507509
508- // scan the table
509- while (true)
510+ /* for each row */
511+ while (true)
510512 {
511- graphid startid , endid ;
513+ graphid startid ;
514+ graphid endid ;
512515 bool isNull ;
516+ bool found_startid = false;
517+ bool found_endid = false;
513518
514519 tuple = heap_getnext (scan_desc , ForwardScanDirection );
515520
516- // no more tuples to process, break and scan the next label.
521+ /* no more tuples to process, break and scan the next label. */
517522 if (!HeapTupleIsValid (tuple ))
523+ {
518524 break ;
525+ }
519526
520527 ExecStoreHeapTuple (tuple , slot , false);
521528
522- startid = GRAPHID_GET_DATUM (slot_getattr (slot , Anum_ag_label_edge_table_start_id , & isNull ));
523- endid = GRAPHID_GET_DATUM (slot_getattr (slot , Anum_ag_label_edge_table_end_id , & isNull ));
529+ startid = GRAPHID_GET_DATUM (slot_getattr (
530+ slot , Anum_ag_label_edge_table_start_id , & isNull ));
531+ endid = GRAPHID_GET_DATUM (
532+ slot_getattr (slot , Anum_ag_label_edge_table_end_id , & isNull ));
533+
534+ hash_search (css -> vertex_id_htab , (void * )& startid , HASH_FIND ,
535+ & found_startid );
524536
525- if (id == startid || id == endid )
537+ if (! found_startid )
526538 {
527- /*
528- * We have found an edge that uses the vertex. Either delete the
529- * edge or throw an error. Depending on whether the DETACH
530- * option was specified in the query.
531- */
532- if (detach_delete )
539+ hash_search (css -> vertex_id_htab , (void * )& endid , HASH_FIND ,
540+ & found_endid );
541+ }
542+
543+ if (found_startid || found_endid )
544+ {
545+ if (css -> delete_data -> detach )
546+ {
533547 delete_entity (estate , resultRelInfo , tuple );
548+ }
534549 else
535- ereport (ERROR ,
536- (errcode (ERRCODE_INTERNAL_ERROR ),
537- errmsg ("Cannot delete vertex %s, because it still has edges attached. "
538- "To delete this vertex, you must first delete the attached edges." ,
539- var_name )));
550+ {
551+ ereport (
552+ ERROR ,
553+ (errcode (ERRCODE_INTERNAL_ERROR ),
554+ errmsg (
555+ "Cannot delete a vertex that has edge(s). "
556+ "Delete the edge(s) first, or try DETACH DELETE." )));
557+ }
540558 }
541559 }
542560
543561 table_endscan (scan_desc );
544562 destroy_entity_result_rel_info (resultRelInfo );
545563 }
546-
547- Decrement_Estate_CommandId (estate );
548564}
0 commit comments