Skip to content

Instantly share code, notes, and snippets.

@PumpkinSeed
Last active June 11, 2023 12:56
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save PumpkinSeed/b4993c6ad20ea90e3da8c991a90a91e1 to your computer and use it in GitHub Desktop.
Save PumpkinSeed/b4993c6ad20ea90e3da8c991a90a91e1 to your computer and use it in GitHub Desktop.
Extension for sql package
func structScan(rows *sql.Rows, model interface{}) error {
v := reflect.ValueOf(model)
if v.Kind() != reflect.Ptr {
return errors.New("must pass a pointer, not a value, to StructScan destination") // @todo add new error message
}
v = reflect.Indirect(v)
t := v.Type()
cols, _ := rows.Columns()
var m map[string]interface{}
for rows.Next() {
columns := make([]interface{}, len(cols))
columnPointers := make([]interface{}, len(cols))
for i := range columns {
columnPointers[i] = &columns[i]
}
if err := rows.Scan(columnPointers...); err != nil {
return err
}
m = make(map[string]interface{})
for i, colName := range cols {
val := columnPointers[i].(*interface{})
m[colName] = *val
}
}
for i := 0; i < v.NumField(); i++ {
field := strings.Split(t.Field(i).Tag.Get("json"), ",")[0]
if item, ok := m[field]; ok {
if v.Field(i).CanSet() {
if item != nil {
switch v.Field(i).Kind() {
case reflect.String:
v.Field(i).SetString(b2s(item.([]uint8)))
case reflect.Float32, reflect.Float64:
v.Field(i).SetFloat(item.(float64))
case reflect.Ptr:
if reflect.ValueOf(item).Kind() == reflect.Bool {
itemBool := item.(bool)
v.Field(i).Set(reflect.ValueOf(&itemBool))
}
case reflect.Struct:
v.Field(i).Set(reflect.ValueOf(item))
default:
fmt.Println(t.Field(i).Name, ": ", v.Field(i).Kind(), " - > - ", reflect.ValueOf(item).Kind()) // @todo remove after test out the Get methods
}
}
}
}
}
return nil
}
@jesseorr
Copy link

can you please give the function b2s as stated in line 40 av.Field(i).SetString(b2s(item.([]uint8)))

This one seemed to work.

func b2s(b []byte) string {
	/* #nosec G103 */
	return *(*string)(unsafe.Pointer(&b))
}

@jesseorr
Copy link

I added another case statement to deal with MySQL boolean values being 0/1, and wanting to utilize bool values throughout the code.

					case reflect.Bool:
						v.Field(i).Set(reflect.ValueOf(!(item.(int64) == 0)))

@Indribell
Copy link

Here is a more up to date version of the above code:

https://gist.github.com/Indribell/91c7504460732e4958bec2a80ab7b3f6

Bug fixes, performance fixes, it can now handle Struct or Slice/Struct, ... Not perfect ( can be made faster with struct information caching instead of reflecting ) but that is all the time we have today.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment