From 8ed0dbe4201a58b00d6f3743178f4cbe5328e2b0 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Thu, 6 Mar 2025 15:17:57 +0800 Subject: *: Support subgroups via SQL recursion --- http_handle_group_index.go | 118 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 112 insertions(+), 6 deletions(-) (limited to 'http_handle_group_index.go') diff --git a/http_handle_group_index.go b/http_handle_group_index.go index 09a85f8..bbdff46 100644 --- a/http_handle_group_index.go +++ b/http_handle_group_index.go @@ -4,21 +4,127 @@ package main import ( + "fmt" "net/http" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgtype" ) -func handle_group_repos(w http.ResponseWriter, r *http.Request, params map[string]any) { - var group_name string +func handle_group_index(w http.ResponseWriter, r *http.Request, params map[string]any) { + var group_path []string var repos []name_desc_t + var subgroups []name_desc_t var err error - group_name = params["group_name"].(string) - repos, err = query_name_desc_list(r.Context(), "SELECT r.name, COALESCE(r.description, '') FROM repos r JOIN groups g ON r.group_id = g.id WHERE g.name = $1;", group_name) + group_path = params["group_path"].([]string) + + // Repos + var rows pgx.Rows + rows, err = database.Query(r.Context(), ` + WITH RECURSIVE group_path_cte AS ( + SELECT + id, + parent_group, + name, + 1 AS depth + FROM groups + WHERE name = ($1::text[])[1] + AND parent_group IS NULL + + UNION ALL + + SELECT + g.id, + g.parent_group, + g.name, + group_path_cte.depth + 1 + FROM groups g + JOIN group_path_cte ON g.parent_group = group_path_cte.id + WHERE g.name = ($1::text[])[group_path_cte.depth + 1] + AND group_path_cte.depth + 1 <= cardinality($1::text[]) + ) + SELECT r.name, COALESCE(r.description, '') + FROM group_path_cte c + JOIN repos r ON r.group_id = c.id + WHERE c.depth = cardinality($1::text[]) + `, + pgtype.FlatArray[string](group_path), + ) if err != nil { - http.Error(w, "Error getting groups: "+err.Error(), http.StatusInternalServerError) + http.Error(w, "Error getting repos: "+err.Error(), http.StatusInternalServerError) return } + defer rows.Close() + + for rows.Next() { + var name, description string + if err = rows.Scan(&name, &description); err != nil { + http.Error(w, "Error getting repos: "+err.Error(), http.StatusInternalServerError) + return + } + repos = append(repos, name_desc_t{name, description}) + } + if err = rows.Err(); err != nil { + http.Error(w, "Error getting repos: "+err.Error(), http.StatusInternalServerError) + return + } + + // Subgroups + rows, err = database.Query(r.Context(), ` + WITH RECURSIVE group_path_cte AS ( + SELECT + id, + parent_group, + name, + 1 AS depth + FROM groups + WHERE name = ($1::text[])[1] + AND parent_group IS NULL + + UNION ALL + + SELECT + g.id, + g.parent_group, + g.name, + group_path_cte.depth + 1 + FROM groups g + JOIN group_path_cte ON g.parent_group = group_path_cte.id + WHERE g.name = ($1::text[])[group_path_cte.depth + 1] + AND group_path_cte.depth + 1 <= cardinality($1::text[]) + ) + SELECT g.name, COALESCE(g.description, '') + FROM group_path_cte c + JOIN groups g ON g.parent_group = c.id + WHERE c.depth = cardinality($1::text[]) + `, + pgtype.FlatArray[string](group_path), + ) + if err != nil { + http.Error(w, "Error getting subgroups: "+err.Error(), http.StatusInternalServerError) + return + } + defer rows.Close() + + for rows.Next() { + var name, description string + if err = rows.Scan(&name, &description); err != nil { + http.Error(w, "Error getting subgroups: "+err.Error(), http.StatusInternalServerError) + return + } + subgroups = append(subgroups, name_desc_t{name, description}) + } + if err = rows.Err(); err != nil { + http.Error(w, "Error getting subgroups: "+err.Error(), http.StatusInternalServerError) + return + } + params["repos"] = repos + params["subgroups"] = subgroups - render_template(w, "group_repos", params) + fmt.Println(group_path) + + render_template(w, "group", params) } + -- cgit v1.2.3