github.com/octohelm/storage@v0.0.0-20240516030302-1ac2cc1ea347/internal/sql/scanner/scan.go (about) 1 package scanner 2 3 import ( 4 "context" 5 "database/sql" 6 7 "github.com/pkg/errors" 8 9 "github.com/octohelm/storage/pkg/dberr" 10 ) 11 12 func Scan(ctx context.Context, rows *sql.Rows, v interface{}) error { 13 if rows == nil { 14 return nil 15 } 16 defer func() { 17 _ = rows.Close() 18 }() 19 20 si, err := ScanIteratorFor(v) 21 if err != nil { 22 return err 23 } 24 25 for rows.Next() { 26 item := si.New() 27 28 if scanErr := tryScan(ctx, rows, item); scanErr != nil { 29 if errors.Is(err, context.Canceled) { 30 return nil 31 } 32 return scanErr 33 } 34 35 if err := si.Next(item); err != nil { 36 return err 37 } 38 } 39 40 if mustHasRecord, ok := si.(interface{ MustHasRecord() bool }); ok { 41 if !mustHasRecord.MustHasRecord() { 42 return dberr.New(dberr.ErrTypeNotFound, "record is not found") 43 } 44 } 45 46 if err := rows.Err(); err != nil { 47 return err 48 } 49 50 // Make sure the query can be processed to completion with no errors. 51 if err := rows.Close(); err != nil { 52 return err 53 } 54 55 return nil 56 } 57 58 func tryScan(ctx context.Context, rows *sql.Rows, item any) error { 59 done := make(chan error) 60 go func() { 61 defer close(done) 62 63 if err := scanTo(ctx, rows, item); err != nil { 64 done <- err 65 } 66 }() 67 68 select { 69 case <-ctx.Done(): 70 return ctx.Err() 71 default: 72 return <-done 73 } 74 75 }