-
Notifications
You must be signed in to change notification settings - Fork 3
/
row.go
104 lines (86 loc) · 2.55 KB
/
row.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package scan
import (
"fmt"
"reflect"
)
var zeroValue reflect.Value
func wrapRows(r Rows, allowUnknown bool) (*Row, error) {
cols, err := r.Columns()
if err != nil {
return nil, err
}
return &Row{
r: r,
columns: cols,
scanDestinations: make([]reflect.Value, len(cols)),
allowUnknown: allowUnknown,
}, nil
}
// Row represents a single row from the query and is passed to the [BeforeFunc]
// when sent to a mapper's before function, scans should be scheduled
// with either the [ScheduleScan] or [ScheduleScanx] methods
type Row struct {
r Rows
columns []string
scanDestinations []reflect.Value
unknownDestinations []string
allowUnknown bool
}
// ScheduleScan schedules a scan for the column name into the given value
// val should be a pointer
func (r *Row) ScheduleScan(colName string, val any) {
r.ScheduleScanx(colName, reflect.ValueOf(val))
}
// ScheduleScanx schedules a scan for the column name into the given reflect.Value
// val.Kind() should be reflect.Pointer
func (r *Row) ScheduleScanx(colName string, val reflect.Value) {
for i, n := range r.columns {
if n == colName {
r.scanDestinations[i] = val
return
}
}
r.unknownDestinations = append(r.unknownDestinations, colName)
}
// To get a copy of the columns to pass to mapper generators
// since modifing the map can have unintended side effects.
// Ideally, a generator should only call this once
func (r *Row) columnsCopy() []string {
m := make([]string, len(r.columns))
copy(m, r.columns)
return m
}
func (r *Row) scanCurrentRow() error {
if len(r.unknownDestinations) > 0 {
return createError(fmt.Errorf("unknown columns to map to: %v", r.unknownDestinations), r.unknownDestinations...)
}
targets, err := r.createTargets()
if err != nil {
return err
}
err = r.r.Scan(targets...)
if err != nil {
return err
}
r.scanDestinations = make([]reflect.Value, len(r.columns))
return nil
}
func (r *Row) createTargets() ([]any, error) {
targets := make([]any, len(r.columns))
for i, name := range r.columns {
dest := r.scanDestinations[i]
if dest != zeroValue {
targets[i] = dest.Interface()
continue
}
if !r.allowUnknown {
err := fmt.Errorf("No destination for column %s", name)
return nil, createError(err, "no destination", name)
}
// See https://github.com/golang/go/issues/41607:
// Some drivers cannot work with nil values, so valid pointers should be
// used for all column targets, even if they are discarded afterwards.
targets[i] = new(interface{})
}
return targets, nil
}