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  }