github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/dashboard/app/entities_spanner.go (about) 1 // Copyright 2024 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package main 5 6 import ( 7 "context" 8 "fmt" 9 10 "cloud.google.com/go/civil" 11 "cloud.google.com/go/spanner" 12 "github.com/google/syzkaller/pkg/coveragedb" 13 "github.com/google/syzkaller/pkg/coveragedb/spannerclient" 14 "google.golang.org/api/iterator" 15 ) 16 17 // This file contains definitions of entities stored in spanner. 18 19 type CoverageHistory struct { 20 instrumented map[string]int64 21 covered map[string]int64 22 periods map[coveragedb.TimePeriod]struct{} 23 } 24 25 // MergedCoverage uses dates, not time. 26 func MergedCoverage(ctx context.Context, client spannerclient.SpannerClient, ns, periodType string, 27 ) (*CoverageHistory, error) { 28 minDays, maxDays, err := coveragedb.MinMaxDays(periodType) 29 if err != nil { 30 return nil, fmt.Errorf("coveragedb.MinMaxDays: %w", err) 31 } 32 pOps, err := coveragedb.PeriodOps(periodType) 33 if err != nil { 34 return nil, fmt.Errorf("coveragedb.PeriodOps: %w", err) 35 } 36 stmt := spanner.Statement{ 37 SQL: ` 38 select 39 dateto as targetdate, 40 duration as days, 41 cast(sum(instrumented) as INTEGER) as instrumented, 42 cast(sum(covered) as INTEGER) as covered 43 from merge_history join files 44 on merge_history.session = files.session 45 where namespace=$1 and duration>=$2 and duration<=$3 and manager='*' 46 group by dateto, duration`, 47 Params: map[string]interface{}{ 48 "p1": ns, 49 "p2": minDays, 50 "p3": maxDays, 51 }, 52 } 53 54 iter := client.Single().Query(ctx, stmt) 55 defer iter.Stop() 56 res := &CoverageHistory{ 57 instrumented: map[string]int64{}, 58 covered: map[string]int64{}, 59 periods: map[coveragedb.TimePeriod]struct{}{}, 60 } 61 for { 62 row, err := iter.Next() 63 if err == iterator.Done { 64 break 65 } 66 if err != nil { 67 return nil, fmt.Errorf("failed to iter.Next() spanner DB: %w", err) 68 } 69 var r struct { 70 Targetdate civil.Date 71 Days int64 72 Instrumented int64 73 Covered int64 74 } 75 if err = row.ToStruct(&r); err != nil { 76 return nil, fmt.Errorf("failed to row.ToStruct() spanner DB: %w", err) 77 } 78 period := coveragedb.TimePeriod{DateTo: r.Targetdate, Days: int(r.Days)} 79 if !pOps.IsValidPeriod(period) { 80 continue 81 } 82 res.instrumented[r.Targetdate.String()] = r.Instrumented 83 res.covered[r.Targetdate.String()] = r.Covered 84 if _, found := res.periods[period]; found { 85 return nil, fmt.Errorf("db error: only one period expected for date %s, days %d", 86 period.DateTo.String(), period.Days) 87 } 88 res.periods[period] = struct{}{} 89 } 90 return res, nil 91 }