@@ -4,9 +4,12 @@ import (
44 "bytes"
55 "database/sql/driver"
66 "encoding/csv"
7+ "errors"
78 "fmt"
89 "io"
10+ "reflect"
911 "strings"
12+ "time"
1013)
1114
1215const invalidate = "☠☠☠ MEMORY OVERWRITTEN ☠☠☠ "
@@ -127,6 +130,133 @@ type Rows struct {
127130 closeErr error
128131}
129132
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+
130260// NewRows allows Rows to be created from a
131261// sql driver.Value slice or from the CSV string and
132262// to be used as sql driver.Rows.
0 commit comments