diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2026-01-13 12:59:57 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2026-01-13 13:21:16 +0000 |
| commit | 787e7cd0a324014b6ec3d4d830119c8d71439200 (patch) | |
| tree | b0c8ed6bfefafda2e812dc30f0451ec0d2bf6e1d | |
| parent | e31fcc98a0656b952c90a2bcf6d044efb890e21a (diff) | |
dashboard/app: prepare for spanner migrations
If the code uses "select *", it's not possible to update spanner schema.
Adding a field to spanner first leads to "missing field in Go struct" errors,
adding a field to Go struct first leads to "missing field in spanner" errors.
Replace "select *" with concrete set of fields the code knowns about.
This should allow adding fields to spanner first.
| -rw-r--r-- | dashboard/app/aidb/crud.go | 33 |
1 files changed, 27 insertions, 6 deletions
diff --git a/dashboard/app/aidb/crud.go b/dashboard/app/aidb/crud.go index ccecc8e3f..a01c370b6 100644 --- a/dashboard/app/aidb/crud.go +++ b/dashboard/app/aidb/crud.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "reflect" + "strings" "time" "cloud.google.com/go/spanner" @@ -28,7 +29,7 @@ func init() { func LoadWorkflows(ctx context.Context) ([]*Workflow, error) { return selectAll[Workflow](ctx, spanner.Statement{ - SQL: `SELECT * FROM Workflows`, + SQL: selectWorkflows(), }) } @@ -113,7 +114,7 @@ func StartJob(ctx context.Context, req *dashapi.AIJobPollReq) (*Job, error) { _, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error { { iter := txn.Query(ctx, spanner.Statement{ - SQL: `SELECT * FROM Jobs WHERE Workflow IN UNNEST(@workflows) + SQL: selectJobs() + `WHERE Workflow IN UNNEST(@workflows) AND Started IS NULL ORDER BY Created ASC LIMIT 1`, Params: map[string]any{ @@ -144,7 +145,7 @@ func StartJob(ctx context.Context, req *dashapi.AIJobPollReq) (*Job, error) { func LoadNamespaceJobs(ctx context.Context, ns string) ([]*Job, error) { return selectAll[Job](ctx, spanner.Statement{ - SQL: `SELECT * FROM Jobs WHERE Namespace = @ns ORDER BY Created DESC`, + SQL: selectJobs() + `WHERE Namespace = @ns ORDER BY Created DESC`, Params: map[string]any{ "ns": ns, }, @@ -153,7 +154,7 @@ func LoadNamespaceJobs(ctx context.Context, ns string) ([]*Job, error) { func LoadBugJobs(ctx context.Context, bugID string) ([]*Job, error) { return selectAll[Job](ctx, spanner.Statement{ - SQL: `SELECT * FROM Jobs WHERE BugID = @bugID ORDER BY Created DESC`, + SQL: selectJobs() + `WHERE BugID = @bugID ORDER BY Created DESC`, Params: map[string]any{ "bugID": bugID, }, @@ -162,7 +163,7 @@ func LoadBugJobs(ctx context.Context, bugID string) ([]*Job, error) { func LoadJob(ctx context.Context, id string) (*Job, error) { return selectOne[Job](ctx, spanner.Statement{ - SQL: `SELECT * FROM Jobs WHERE ID = @id`, + SQL: selectJobs() + `WHERE ID = @id`, Params: map[string]any{ "id": id, }, @@ -201,7 +202,7 @@ func StoreTrajectorySpan(ctx context.Context, jobID string, span *trajectory.Spa func LoadTrajectory(ctx context.Context, jobID string) ([]*TrajectorySpan, error) { return selectAll[TrajectorySpan](ctx, spanner.Statement{ - SQL: `SELECT * FROM TrajectorySpans WHERE JobID = @job_id ORDER BY Seq ASC`, + SQL: selectTrajectorySpans() + `WHERE JobID = @job_id ORDER BY Seq ASC`, Params: map[string]any{ "job_id": jobID, }, @@ -252,6 +253,26 @@ var TimeNow = func(ctx context.Context) time.Time { return time.Now() } +func selectWorkflows() string { + return selectAllFrom[Workflow]("Workflows") +} + +func selectJobs() string { + return selectAllFrom[Job]("Jobs") +} + +func selectTrajectorySpans() string { + return selectAllFrom[TrajectorySpan]("TrajectorySpans") +} + +func selectAllFrom[T any](table string) string { + var fields []string + for _, field := range reflect.VisibleFields(reflect.TypeFor[T]()) { + fields = append(fields, field.Name) + } + return fmt.Sprintf("SELECT %v FROM %v ", strings.Join(fields, ", "), table) +} + func toNullJSON(v map[string]any) spanner.NullJSON { if v == nil { return spanner.NullJSON{} |
