@@ -4,9 +4,12 @@ import (
4
4
"bytes"
5
5
"database/sql/driver"
6
6
"encoding/csv"
7
+ "errors"
7
8
"fmt"
8
9
"io"
10
+ "reflect"
9
11
"strings"
12
+ "time"
10
13
)
11
14
12
15
const invalidate = "☠☠☠ MEMORY OVERWRITTEN ☠☠☠ "
@@ -127,6 +130,133 @@ type Rows struct {
127
130
closeErr error
128
131
}
129
132
133
+ // NewRowsFromStruct new Rows from struct reflect with tagName
134
+ // tagName default "json"
135
+ func NewRowsFromStruct (m interface {}, tagName ... string ) (* Rows , error ) {
136
+ if m == nil {
137
+ return nil , errors .New ("param m is nil" )
138
+ }
139
+ val := reflect .ValueOf (m ).Elem ()
140
+ if val .Kind () != reflect .Struct {
141
+ return nil , errors .New ("param type must be struct" )
142
+ }
143
+ num := val .NumField ()
144
+ if num == 0 {
145
+ return nil , errors .New ("no properties available" )
146
+ }
147
+ columns := make ([]string , 0 , num )
148
+ var values []driver.Value
149
+ tag := "json"
150
+ if len (tagName ) > 0 {
151
+ tag = tagName [0 ]
152
+ }
153
+ for i := 0 ; i < num ; i ++ {
154
+ f := val .Type ().Field (i )
155
+ column := f .Tag .Get (tag )
156
+ if len (column ) > 0 {
157
+ columns = append (columns , column )
158
+ values = append (values , val .Field (i ))
159
+ }
160
+ }
161
+ if len (columns ) == 0 {
162
+ return nil , errors .New ("tag not match" )
163
+ }
164
+ rows := & Rows {
165
+ cols : columns ,
166
+ nextErr : make (map [int ]error ),
167
+ converter : reflectTypeConverter {},
168
+ }
169
+ return rows .AddRow (values ... ), nil
170
+ }
171
+
172
+ var timeKind = reflect .TypeOf (time.Time {}).Kind ()
173
+
174
+ type reflectTypeConverter struct {}
175
+
176
+ func (reflectTypeConverter ) ConvertValue (v interface {}) (driver.Value , error ) {
177
+ rv := v .(reflect.Value )
178
+ switch rv .Kind () {
179
+ case reflect .Ptr :
180
+ // indirect pointers
181
+ if rv .IsNil () {
182
+ return nil , nil
183
+ } else {
184
+ return driver .DefaultParameterConverter .ConvertValue (rv .Elem ().Interface ())
185
+ }
186
+ case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
187
+ return rv .Int (), nil
188
+ case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 :
189
+ return int64 (rv .Uint ()), nil
190
+ case reflect .Uint64 :
191
+ u64 := rv .Uint ()
192
+ if u64 >= 1 << 63 {
193
+ return nil , fmt .Errorf ("uint64 values with high bit set are not supported" )
194
+ }
195
+ return int64 (u64 ), nil
196
+ case reflect .Float32 , reflect .Float64 :
197
+ return rv .Float (), nil
198
+ case reflect .Bool :
199
+ return rv .Bool (), nil
200
+ case reflect .Slice :
201
+ ek := rv .Type ().Elem ().Kind ()
202
+ if ek == reflect .Uint8 {
203
+ return rv .Bytes (), nil
204
+ }
205
+ return nil , fmt .Errorf ("unsupported type %T, a slice of %s" , v , ek )
206
+ case reflect .String :
207
+ return rv .String (), nil
208
+ case timeKind :
209
+ return rv .Interface ().(time.Time ), nil
210
+ }
211
+ return nil , fmt .Errorf ("unsupported type %T, a %s" , v , rv .Kind ())
212
+ }
213
+
214
+ // NewRowsFromStructs new Rows from struct slice reflect with tagName
215
+ // NOTE: arr must be of the same type
216
+ // tagName default "json"
217
+ func NewRowsFromStructs (tagName string , arr ... interface {}) (* Rows , error ) {
218
+ if len (arr ) == 0 {
219
+ return nil , errors .New ("param arr is nil" )
220
+ }
221
+ typ := reflect .TypeOf (arr [0 ]).Elem ()
222
+ if typ .Kind () != reflect .Struct {
223
+ return nil , errors .New ("param type must be struct" )
224
+ }
225
+ if typ .NumField () == 0 {
226
+ return nil , errors .New ("no properties available" )
227
+ }
228
+ var columns []string
229
+ tag := "json"
230
+ if len (tagName ) > 0 {
231
+ tag = tagName
232
+ }
233
+ for i := 0 ; i < typ .NumField (); i ++ {
234
+ f := typ .Field (i )
235
+ column := f .Tag .Get (tag )
236
+ if len (column ) > 0 {
237
+ columns = append (columns , column )
238
+ }
239
+ }
240
+ if len (columns ) == 0 {
241
+ return nil , errors .New ("tag not match" )
242
+ }
243
+ rows := & Rows {
244
+ cols : columns ,
245
+ nextErr : make (map [int ]error ),
246
+ converter : reflectTypeConverter {},
247
+ }
248
+ for _ , m := range arr {
249
+ v := m
250
+ val := reflect .ValueOf (v ).Elem ()
251
+ var values []driver.Value
252
+ for _ , column := range columns {
253
+ values = append (values , val .FieldByName (column ))
254
+ }
255
+ rows .AddRow (values ... )
256
+ }
257
+ return rows , nil
258
+ }
259
+
130
260
// NewRows allows Rows to be created from a
131
261
// sql driver.Value slice or from the CSV string and
132
262
// to be used as sql driver.Rows.
0 commit comments