github.com/kunlun-qilian/sqlx/v3@v3.0.0/scanner/struct.go (about)

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