github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/syz-cluster/pkg/db/report_reply_repo.go (about)

     1  // Copyright 2025 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 db
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  
    10  	"cloud.google.com/go/spanner"
    11  )
    12  
    13  type ReportReplyRepository struct {
    14  	client *spanner.Client
    15  }
    16  
    17  func NewReportReplyRepository(client *spanner.Client) *ReportReplyRepository {
    18  	return &ReportReplyRepository{
    19  		client: client,
    20  	}
    21  }
    22  
    23  func (repo *ReportReplyRepository) FindParentReportID(ctx context.Context, reporter, messageID string) (string, error) {
    24  	type result struct {
    25  		ReportID string `spanner:"ReportID"`
    26  	}
    27  	ret, err := readEntity[result](ctx, repo.client.Single(), spanner.Statement{
    28  		SQL: "SELECT `ReportReplies`.ReportID FROM `ReportReplies` " +
    29  			"JOIN `SessionReports` ON `SessionReports`.ID = `ReportReplies`.ReportID " +
    30  			"WHERE `ReportReplies`.MessageID = @messageID " +
    31  			"AND `SessionReports`.Reporter = @reporter LIMIT 1",
    32  		Params: map[string]interface{}{
    33  			"reporter":  reporter,
    34  			"messageID": messageID,
    35  		},
    36  	})
    37  	if err != nil {
    38  		return "", err
    39  	} else if ret != nil {
    40  		return ret.ReportID, nil
    41  	}
    42  	return "", nil
    43  }
    44  
    45  var ErrReportReplyExists = errors.New("the reply has already been recorded")
    46  
    47  func (repo *ReportReplyRepository) Insert(ctx context.Context, reply *ReportReply) error {
    48  	_, err := repo.client.ReadWriteTransaction(ctx,
    49  		func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
    50  			entity, err := readEntity[ReportReply](ctx, txn, spanner.Statement{
    51  				SQL: "SELECT * from `ReportReplies` " +
    52  					"WHERE `ReportID`=@reportID AND `MessageID`=@messageID",
    53  				Params: map[string]interface{}{
    54  					"reportID":  reply.ReportID,
    55  					"messageID": reply.MessageID,
    56  				},
    57  			})
    58  			if err != nil {
    59  				return err
    60  			} else if entity != nil {
    61  				return ErrReportReplyExists
    62  			}
    63  			insert, err := spanner.InsertStruct("ReportReplies", reply)
    64  			if err != nil {
    65  				return err
    66  			}
    67  			return txn.BufferWrite([]*spanner.Mutation{insert})
    68  		})
    69  	return err
    70  }
    71  
    72  func (repo *ReportReplyRepository) LastForReporter(ctx context.Context, reporter string) (*ReportReply, error) {
    73  	return readEntity[ReportReply](ctx, repo.client.Single(), spanner.Statement{
    74  		SQL: "SELECT `ReportReplies`.* FROM `ReportReplies` " +
    75  			"JOIN `SessionReports` ON `SessionReports`.ID=`ReportReplies`.ReportID " +
    76  			"WHERE `SessionReports`.Reporter=@reporter " +
    77  			"ORDER BY `Time` DESC LIMIT 1",
    78  		Params: map[string]interface{}{
    79  			"reporter": reporter,
    80  		},
    81  	})
    82  }