github.com/octohelm/storage@v0.0.0-20240516030302-1ac2cc1ea347/internal/sql/scanner/struct.go (about)

     1  package scanner
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  	"fmt"
     7  	"reflect"
     8  	"strings"
     9  
    10  	"github.com/octohelm/storage/pkg/sqlbuilder"
    11  
    12  	"github.com/octohelm/storage/internal/sql/scanner/nullable"
    13  	reflectx "github.com/octohelm/x/reflect"
    14  )
    15  
    16  func scanTo(ctx context.Context, rows *sql.Rows, v interface{}) error {
    17  	tpe := reflect.TypeOf(v)
    18  
    19  	if tpe.Kind() != reflect.Ptr {
    20  		return fmt.Errorf("scanTo target must be a ptr value, but got %T", v)
    21  	}
    22  
    23  	if s, ok := v.(sql.Scanner); ok {
    24  		return rows.Scan(s)
    25  	}
    26  
    27  	tpe = reflectx.Deref(tpe)
    28  
    29  	switch tpe.Kind() {
    30  	case reflect.Struct:
    31  		columns, err := rows.Columns()
    32  		if err != nil {
    33  			return err
    34  		}
    35  
    36  		n := len(columns)
    37  		if n < 1 {
    38  			return nil
    39  		}
    40  
    41  		dest := make([]interface{}, n)
    42  		holder := placeholder()
    43  
    44  		columnIndexes := map[string]int{}
    45  
    46  		for i, columnName := range columns {
    47  			columnIndexes[strings.ToLower(columnName)] = i
    48  			dest[i] = holder
    49  		}
    50  
    51  		sqlbuilder.ForEachStructFieldValue(ctx, v, func(sf *sqlbuilder.StructFieldValue) {
    52  			if sf.TableName != "" {
    53  				if i, ok := columnIndexes[sf.TableName+"__"+sf.Field.Name]; ok && i > -1 {
    54  					dest[i] = nullable.NewNullIgnoreScanner(sf.Value.Addr().Interface())
    55  				}
    56  			}
    57  
    58  			if i, ok := columnIndexes[sf.Field.Name]; ok && i > -1 {
    59  				dest[i] = nullable.NewNullIgnoreScanner(sf.Value.Addr().Interface())
    60  			}
    61  		})
    62  
    63  		return rows.Scan(dest...)
    64  	default:
    65  		return rows.Scan(nullable.NewNullIgnoreScanner(v))
    66  	}
    67  }
    68  
    69  func placeholder() sql.Scanner {
    70  	p := emptyScanner(0)
    71  	return &p
    72  }
    73  
    74  type emptyScanner int
    75  
    76  func (e *emptyScanner) Scan(value interface{}) error {
    77  	return nil
    78  }