-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscanner.go
More file actions
98 lines (83 loc) · 2.02 KB
/
scanner.go
File metadata and controls
98 lines (83 loc) · 2.02 KB
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
package sqlxt
import (
"database/sql"
"fmt"
"reflect"
"github.com/avalchev94/sqlxt/internal/builder"
)
// Scanner is a type that will Scan your query's result.
type Scanner struct {
rows *sql.Rows
err error
}
// NewScanner is a function for creating new Scanner.
// Have in mind that Scanner is always successfully created, but
// later Scan method could fail because of problems is 'rows'.
func NewScanner(rows *sql.Rows, err error) *Scanner {
return &Scanner{rows, err}
}
// Scan is the 'meat' of the package. It scan the 'rows' input into
// the 'dest' parameter. Dest variable could be:
// - primitive type(string, int, bool, interface{})
// - struct (with or without 'sql' tags);
// - map with key(int, string, interface{});
// - slice in combination with some of the above types;
func (s *Scanner) Scan(dest interface{}) error {
switch {
case s.err != nil:
return s.err
case s.rows == nil:
return fmt.Errorf(`sqlxt: sql "rows" is nil`)
case s.rows.Err() != nil:
return s.rows.Err()
}
builder, err := builder.New(dest)
if err != nil {
return err
}
if builder.OneRowExpected() {
if !s.rows.Next() {
return sql.ErrNoRows
}
return s.scanOneRow(builder)
}
return s.scanAllRows(builder)
}
func (s *Scanner) scanAllRows(builder *builder.Builder) error {
rowsCount := 0
for s.rows.Next() {
rowBuilder, err := builder.Next()
if err != nil {
return err
}
err = s.scanOneRow(rowBuilder)
if err != nil {
return err
}
err = builder.Add(rowBuilder)
if err != nil {
return err
}
rowsCount++
}
if rowsCount == 0 {
return sql.ErrNoRows
}
return nil
}
func (s *Scanner) scanOneRow(builder *builder.Builder) error {
columnTypes, err := s.rows.ColumnTypes()
if err != nil {
return err
}
params, err := builder.Parameters(columnTypes)
if err != nil {
return err
}
result := reflect.ValueOf(s.rows.Scan).Call(params)
// database/sql Scan returns only one variable - error
if !result[0].IsNil() {
return result[0].Interface().(error)
}
return builder.Update(params, columnTypes)
}