From fe02f4da2ca6092a801aebcc644844fc915aa187 Mon Sep 17 00:00:00 2001 From: kolaente Date: Sun, 3 Mar 2024 11:40:30 +0100 Subject: [PATCH] fix(project): check for project nesting cycles with a single recursive cte instead of a loop --- pkg/models/project.go | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/pkg/models/project.go b/pkg/models/project.go index 0e5992156..376c310c3 100644 --- a/pkg/models/project.go +++ b/pkg/models/project.go @@ -667,12 +667,34 @@ func checkProjectBeforeUpdateOrDelete(s *xorm.Session, project *Project) (err er } } - var parent *Project - parent, err = GetProjectSimpleByID(s, project.ParentProjectID) + allProjects := make(map[int64]*Project) + err = s.SQL(`WITH RECURSIVE all_projects AS ( + SELECT + p.id, + p.parent_project_id + FROM + projects p + WHERE + p.id = ? + UNION ALL + SELECT + p.id, + p.parent_project_id + FROM + Projects p + INNER JOIN all_projects pc ON p.ID = pc.parent_project_id + ) + SELECT + * + FROM + all_projects`, project.ParentProjectID).Find(&allProjects) if err != nil { - return err + return } + var parent *Project + parent = allProjects[project.ParentProjectID] + // Check if there's a cycle in the parent relation parentsVisited := make(map[int64]bool) parentsVisited[project.ID] = true @@ -681,11 +703,7 @@ func checkProjectBeforeUpdateOrDelete(s *xorm.Session, project *Project) (err er break } - // FIXME: Can we do this with better performance? - parent, err = GetProjectSimpleByID(s, parent.ParentProjectID) - if err != nil { - return err - } + parent = allProjects[parent.ParentProjectID] if parentsVisited[parent.ID] { return &ErrProjectCannotHaveACyclicRelationship{