@@ -729,70 +729,61 @@ def _delete_file_scope(conn: kuzu.Connection, filenames: set[str]) -> None:
729729 Skip phantom nodes (filename=""). For Symbol-to-Symbol and UNRESOLVED_AT
730730 edge tables only. Client/Producer/Route edges are handled separately in
731731 pass 5-6 global rebuild.
732- """
733- for filename in filenames :
734- # Delete Symbol-to-Symbol edges by source_file
735- edge_tables = ["EXTENDS" , "IMPLEMENTS" , "INJECTS" , "CALLS" , "DECLARES" , "OVERRIDES" , "UNRESOLVED_AT" ]
736- for edge_type in edge_tables :
737- query = f"""
738- MATCH (src)-[e:{ edge_type } ]->(dst)
739- WHERE e.source_file = $filename
740- DELETE e
741- """
742- conn .execute (query , {"filename" : filename })
743732
744- # Collect Symbol node IDs for this file (for UnresolvedCallSite cleanup)
745- symbol_ids_query = """
746- MATCH (s:Symbol)
747- WHERE s.filename = $filename
748- RETURN s.id
749- """
750- symbol_ids = []
751- result = conn .execute (symbol_ids_query , {"filename" : filename })
752- while result .has_next ():
753- row = result .get_next ()
754- symbol_ids .append (row [0 ]) # Kuzu returns list, not dict
755-
756- # Delete UnresolvedCallSite nodes whose caller_id is in the collected set
757- if symbol_ids :
758- unresolved_query = """
759- MATCH (u:UnresolvedCallSite)
760- WHERE u.caller_id IN $symbol_ids
761- DELETE u
762- """
763- conn .execute (unresolved_query , {"symbol_ids" : symbol_ids })
764-
765- # Delete Symbol nodes
766- delete_symbols_query = """
767- MATCH (s:Symbol)
768- WHERE s.filename = $filename
769- DELETE s
733+ Edges are deleted in batch across all filenames first to avoid Kuzu
734+ "has connected edges" errors when edges from one file point to nodes
735+ in another file within the same scope.
736+ """
737+ filename_list = list (filenames )
738+
739+ # Phase 1: Delete ALL edges from ALL scope files at once.
740+ # This avoids ordering issues where file A has an edge from file B
741+ # pointing into it; if we delete A's nodes before B's edges, Kuzu
742+ # raises "has connected edges" errors.
743+ edge_tables = ["EXTENDS" , "IMPLEMENTS" , "INJECTS" , "CALLS" , "DECLARES" , "OVERRIDES" , "UNRESOLVED_AT" ]
744+ for edge_type in edge_tables :
745+ query = f"""
746+ MATCH (src)-[e:{ edge_type } ]->(dst)
747+ WHERE e.source_file IN $filenames
748+ DELETE e
770749 """
771- conn .execute (delete_symbols_query , {"filename " : filename })
750+ conn .execute (query , {"filenames " : filename_list })
772751
773- # Delete Route nodes (note: EXPOSES edges are deleted in pass 5-6 global rebuild)
774- delete_routes_query = """
775- MATCH (r:Route)
776- WHERE r.filename = $filename
777- DELETE r
752+ # Phase 2: Collect all Symbol node IDs for UnresolvedCallSite cleanup.
753+ symbol_ids : list [str ] = []
754+ symbol_ids_query = """
755+ MATCH (s:Symbol)
756+ WHERE s.filename IN $filenames
757+ RETURN s.id
758+ """
759+ result = conn .execute (symbol_ids_query , {"filenames" : filename_list })
760+ while result .has_next ():
761+ row = result .get_next ()
762+ symbol_ids .append (row [0 ])
763+
764+ # Delete UnresolvedCallSite nodes whose caller_id is in the collected set
765+ if symbol_ids :
766+ unresolved_query = """
767+ MATCH (u:UnresolvedCallSite)
768+ WHERE u.caller_id IN $symbol_ids
769+ DELETE u
778770 """
779- conn .execute (delete_routes_query , {"filename " : filename })
771+ conn .execute (unresolved_query , {"symbol_ids " : symbol_ids })
780772
781- # Delete Client nodes
782- delete_clients_query = """
783- MATCH (c:Client )
784- WHERE c .filename = $filename
785- DELETE c
786- """
787- conn .execute (delete_clients_query , {"filename " : filename })
773+ # Phase 3: Delete Symbol nodes.
774+ delete_symbols_query = """
775+ MATCH (s:Symbol )
776+ WHERE s .filename IN $filenames
777+ DELETE s
778+ """
779+ conn .execute (delete_symbols_query , {"filenames " : filename_list })
788780
789- # Delete Producer nodes
790- delete_producers_query = """
791- MATCH (p:Producer)
792- WHERE p.filename = $filename
793- DELETE p
794- """
795- conn .execute (delete_producers_query , {"filename" : filename })
781+ # Phase 4: Delete Route, Client, Producer nodes.
782+ for label in ["Route" , "Client" , "Producer" ]:
783+ conn .execute (
784+ f"MATCH (n:{ label } ) WHERE n.filename IN $filenames DELETE n" ,
785+ {"filenames" : filename_list },
786+ )
796787
797788
798789def _scoped_write (conn : kuzu .Connection , tables : GraphTables , * , project_root : Path , meta_chain : dict [str , frozenset [str ]] | None ) -> None :
0 commit comments