aboutsummaryrefslogtreecommitdiffstats
path: root/syz-cluster
diff options
context:
space:
mode:
authorPimyn Girgis <pimyn@google.com>2025-12-16 11:04:05 +0100
committerAleksandr Nogikh <nogikh@google.com>2026-01-08 08:55:35 +0000
commitc58cecdc85ba1bbd56a8c0aa39e0e03a34b0aba1 (patch)
tree32f0acb58c9770779790fff11409da2d1f180875 /syz-cluster
parent2a0332308e34a5854141928f86039764bcfa1023 (diff)
syz-cluster: add filtering by series and patch name
Update SeriesFilter in pkg/db to include PatchName and SeriesName fields, implement the SQL logic to filter by these fields case-insensitively, and expose these filters in the dashboard UI.
Diffstat (limited to 'syz-cluster')
-rw-r--r--syz-cluster/dashboard/handler.go1
-rw-r--r--syz-cluster/dashboard/templates/index.html4
-rw-r--r--syz-cluster/pkg/db/migrations/8_add_series_and_patch_tokenization.down.sql5
-rw-r--r--syz-cluster/pkg/db/migrations/8_add_series_and_patch_tokenization.up.sql5
-rw-r--r--syz-cluster/pkg/db/series_repo.go42
5 files changed, 45 insertions, 12 deletions
diff --git a/syz-cluster/dashboard/handler.go b/syz-cluster/dashboard/handler.go
index d8ebd7c61..381efcfcd 100644
--- a/syz-cluster/dashboard/handler.go
+++ b/syz-cluster/dashboard/handler.go
@@ -117,6 +117,7 @@ func (h *dashboardHandler) seriesList(w http.ResponseWriter, r *http.Request) er
WithFindings: r.FormValue("with_findings") != "",
Limit: perPage,
Offset: offset,
+ Name: r.FormValue("name"),
},
// If the filters are changed, the old offset value is irrelevant.
FilterFormURL: urlutil.DropParam(baseURL, "offset", ""),
diff --git a/syz-cluster/dashboard/templates/index.html b/syz-cluster/dashboard/templates/index.html
index b52da738f..afaa87d73 100644
--- a/syz-cluster/dashboard/templates/index.html
+++ b/syz-cluster/dashboard/templates/index.html
@@ -7,6 +7,10 @@
<input type="text" name="cc" class="form-control mb-3" value="{{.Filter.Cc}}" id="inputCc">
</div>
<div class="col-auto col-sm-3">
+ <label for="inputName">Patch/Series Name</label>
+ <input type="text" name="name" class="form-control mb-3" value="{{.Filter.Name}}" id="inputName">
+ </div>
+ <div class="col-auto col-sm-3">
<label for="inputStatus">Status</label>
<select id="inputStatus" class="form-control mb-3" name="status">
{{$filter := .Filter}}
diff --git a/syz-cluster/pkg/db/migrations/8_add_series_and_patch_tokenization.down.sql b/syz-cluster/pkg/db/migrations/8_add_series_and_patch_tokenization.down.sql
new file mode 100644
index 000000000..814f3d40e
--- /dev/null
+++ b/syz-cluster/pkg/db/migrations/8_add_series_and_patch_tokenization.down.sql
@@ -0,0 +1,5 @@
+-- Revert search by patch and series names
+DROP INDEX SeriesIndex;
+ALTER TABLE Series DROP COLUMN TitleTokens;
+DROP INDEX PatchesIndex;
+ALTER TABLE Patches DROP COLUMN TitleTokens;
diff --git a/syz-cluster/pkg/db/migrations/8_add_series_and_patch_tokenization.up.sql b/syz-cluster/pkg/db/migrations/8_add_series_and_patch_tokenization.up.sql
new file mode 100644
index 000000000..b0a1b7afc
--- /dev/null
+++ b/syz-cluster/pkg/db/migrations/8_add_series_and_patch_tokenization.up.sql
@@ -0,0 +1,5 @@
+-- Enable search by patch and series names
+ALTER TABLE Series ADD COLUMN TitleTokens TOKENLIST AS (TOKENIZE_FULLTEXT(Title)) HIDDEN;
+CREATE SEARCH INDEX SeriesIndex ON Series(TitleTokens);
+ALTER TABLE Patches ADD COLUMN TitleTokens TOKENLIST AS (TOKENIZE_FULLTEXT(Title)) HIDDEN;
+CREATE SEARCH INDEX PatchesIndex ON Patches(TitleTokens);
diff --git a/syz-cluster/pkg/db/series_repo.go b/syz-cluster/pkg/db/series_repo.go
index 0adbf3900..fdb452f72 100644
--- a/syz-cluster/pkg/db/series_repo.go
+++ b/syz-cluster/pkg/db/series_repo.go
@@ -9,6 +9,7 @@ import (
"context"
"errors"
"fmt"
+ "strings"
"sync"
"time"
@@ -130,6 +131,7 @@ type SeriesFilter struct {
WithFindings bool
Limit int
Offset int
+ Name string
}
// ListLatest() returns the list of series ordered by the decreasing PublishedAt value.
@@ -139,41 +141,57 @@ func (repo *SeriesRepository) ListLatest(ctx context.Context, filter SeriesFilte
defer ro.Close()
stmt := spanner.Statement{
- SQL: "SELECT Series.* FROM Series WHERE 1=1",
+ SQL: "SELECT Series.* FROM Series",
Params: map[string]any{},
}
+ var conds []string
+
if !maxPublishedAt.IsZero() {
- stmt.SQL += " AND PublishedAt < @toTime"
+ conds = append(conds, "PublishedAt < @toTime")
stmt.Params["toTime"] = maxPublishedAt
}
if filter.Cc != "" {
- stmt.SQL += " AND @cc IN UNNEST(Cc)"
+ conds = append(conds, "@cc IN UNNEST(Cc)")
stmt.Params["cc"] = filter.Cc
}
+ if filter.Name != "" {
+ conds = append(conds, `ID IN(
+SELECT ID FROM SERIES
+WHERE SEARCH(Series.TitleTokens, @name)
+UNION DISTINCT
+SELECT SeriesID FROM Patches
+WHERE SEARCH(Patches.TitleTokens, @name)
+)`)
+ stmt.Params["name"] = filter.Name
+ }
if filter.Status != SessionStatusAny {
// It could have been an INNER JOIN in the main query, but let's favor the simpler code
// in this function.
// The optimizer should transform the query to a JOIN anyway.
- stmt.SQL += " AND EXISTS(SELECT 1 FROM Sessions WHERE"
+ var statusCond = "EXISTS(SELECT 1 FROM Sessions WHERE"
switch filter.Status {
case SessionStatusWaiting:
- stmt.SQL += " Sessions.SeriesID = Series.ID AND Sessions.StartedAt IS NULL"
+ statusCond += " Sessions.SeriesID = Series.ID AND Sessions.StartedAt IS NULL"
case SessionStatusInProgress:
- stmt.SQL += " Sessions.ID = Series.LatestSessionID AND Sessions.FinishedAt IS NULL"
+ statusCond += " Sessions.ID = Series.LatestSessionID AND Sessions.FinishedAt IS NULL"
case SessionStatusFinished:
- stmt.SQL += " Sessions.ID = Series.LatestSessionID AND Sessions.FinishedAt IS NOT NULL" +
+ statusCond += " Sessions.ID = Series.LatestSessionID AND Sessions.FinishedAt IS NOT NULL" +
" AND Sessions.SkipReason IS NULL"
case SessionStatusSkipped:
- stmt.SQL += " Sessions.ID = Series.LatestSessionID AND Sessions.SkipReason IS NOT NULL"
+ statusCond += " Sessions.ID = Series.LatestSessionID AND Sessions.SkipReason IS NOT NULL"
default:
return nil, fmt.Errorf("unknown status value: %q", filter.Status)
}
- stmt.SQL += ")"
+ statusCond += ")"
+ conds = append(conds, statusCond)
}
if filter.WithFindings {
- stmt.SQL += " AND Series.LatestSessionID IS NOT NULL AND EXISTS(" +
- "SELECT 1 FROM Findings WHERE " +
- "Findings.SessionID = Series.LatestSessionID AND Findings.InvalidatedAt IS NULL)"
+ conds = append(conds, "Series.LatestSessionID IS NOT NULL AND EXISTS("+
+ "SELECT 1 FROM Findings WHERE "+
+ "Findings.SessionID = Series.LatestSessionID AND Findings.InvalidatedAt IS NULL)")
+ }
+ if len(conds) != 0 {
+ stmt.SQL += " WHERE " + strings.Join(conds, " AND ")
}
stmt.SQL += " ORDER BY PublishedAt DESC, ID"
if filter.Limit > 0 {