forked from Junedayday/mysqlmapper
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmapper.go
More file actions
141 lines (127 loc) · 3.6 KB
/
mapper.go
File metadata and controls
141 lines (127 loc) · 3.6 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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package mysqlmapper
import (
"database/sql"
"errors"
"fmt"
"reflect"
"strings"
)
const noSQLResult = "no return data from MySQL"
// MapRowsToPointer : Map Mysql result rows to certain pointer
// Parameter 1 - rows : result from MySQL
// Parameter 2 - pointer : must be the pointer of the struct or slice
func MapRowsToPointer(rows *sql.Rows, pointer interface{}) error {
var nameMapperID map[string]int
var oneRowType reflect.Type
var isStruct bool
pointerVal := reflect.ValueOf(pointer)
if reflect.ValueOf(pointer).Kind() != reflect.Ptr {
return fmt.Errorf("input type must be ptr")
} else if reflect.ValueOf(pointer).Elem().Kind() == reflect.Struct {
isStruct = true
nameMapperID = parseStructMemberNames(pointerVal.Type())
oneRowType = pointerVal.Type().Elem()
} else if reflect.ValueOf(pointer).Elem().Kind() == reflect.Slice {
isStruct = false
nameMapperID = parseStructMemberNames(pointerVal.Elem().Type().Elem())
oneRowType = pointerVal.Elem().Type().Elem().Elem()
} else {
return fmt.Errorf("input pointer must point to struct or slice,but %v", reflect.ValueOf(pointer).Elem().Kind())
}
columns, err := rows.Columns()
if err != nil {
return err
}
indexMatch := matchColsToStruct(columns, nameMapperID)
noRowReturn := true
oneRow := make([]interface{}, len(columns))
for j := 0; j < len(oneRow); j++ {
oneRow[j] = new(sql.RawBytes)
}
for rows.Next() {
noRowReturn = false
// Instance a struct for scanning
oneRowStruct := reflect.New(oneRowType).Interface()
s := reflect.ValueOf(oneRowStruct).Elem()
for src, target := range indexMatch {
oneRow[src] = s.Field(target).Addr().Interface()
rows.Scan(oneRow...)
oneRow[src] = new(sql.RawBytes)
}
if isStruct {
reflect.ValueOf(pointer).Elem().Set(reflect.ValueOf(oneRowStruct).Elem())
return nil
}
pointerVal.Elem().Set(reflect.Append(pointerVal.Elem(), reflect.ValueOf(oneRowStruct)))
}
if noRowReturn {
return errors.New(noSQLResult)
}
// set the slice pointerVal to the input pointer
reflect.ValueOf(pointer).Elem().Set(pointerVal.Elem())
return nil
}
// IsEmptyError check the sql result if it is error
func IsEmptyError(err error) bool {
if err != nil && err.Error() == noSQLResult {
return true
}
return false
}
// IsCriticalError check the sql result is error and not nil
func IsCriticalError(err error) bool {
if err != nil && err.Error() != noSQLResult {
return true
}
return false
}
func parseStructMemberNames(pt reflect.Type) map[string]int {
el := pt.Elem()
nameMapperID := make(map[string]int, el.NumField())
for i := 0; i < el.NumField(); i++ {
// parse definition in "json:..." in Tags
// if not find, then transfer name to snake type
js := el.Field(i).Tag.Get("json")
if js == "" {
nameMapperID[snakeString(el.Field(i).Name)] = i
continue
} else if js == "-" {
// "-" means ignore the member value
continue
}
// json tag in proto3 has ","
// like json:"user_addr,omitempty"`
comma := strings.Index(js, ",")
if comma != -1 {
nameMapperID[js[:comma]] = i
} else {
nameMapperID[js] = i
}
}
return nameMapperID
}
func matchColsToStruct(columns []string, mp map[string]int) map[int]int {
structToColumn := make(map[int]int)
for index, name := range columns {
if id, ok := mp[name]; ok {
structToColumn[index] = id
}
}
return structToColumn
}
func snakeString(s string) string {
data := make([]byte, 0, len(s)*2)
j := false
num := len(s)
for i := 0; i < num; i++ {
d := s[i]
if i > 0 && d >= 'A' && d <= 'Z' && j {
data = append(data, '_')
}
if d != '_' {
j = true
}
data = append(data, d)
}
return strings.ToLower(string(data[:]))
}