Reflect on struct passed into interface{} function parameter
When you call BindStruct(&i)
you are passing a pointer to an interface. so this line:
val := reflect.ValueOf(domain).Elem()
will set val to a reflect.Value representing your interface because reflect.ValueOf(domain)
gets a pointer then .Elem()
resolves the interface - which results in your first error as it is indeed an interface value (and they don't have fields):
panic: reflect: call of reflect.Value.NumField on interface Value
So, calling params := BindStruct(i)
would always be correct as you need to pass the actual interface in not a pointer to it.
You don't make clear what the underlying data types being passed into Migrate()
are - are they values or pointers? It's important to know, e.g to inspect struct tags using reflection we need to get back to the struct values type, so the chain goes:
interface -> (pointer ?) -> value -> type
I suspect you are using values as if interface was a value then the line:
val := reflect.ValueOf(domain).Elem()
would be expected to panic since reflect.ValueOf(domain)
would resolve the value and then .Elem()
would try to de-refrence a value.
To be on the safe side we will check the Kind() of the incoming value to make sure we have a struct:
https://play.golang.org/p/6lPOwTd1Q0O
func BindStruct(domain interface{}) (params []interface{}) {
val := reflect.ValueOf(domain) // could be any underlying type
// if its a pointer, resolve its value
if val.Kind() == reflect.Ptr {
val = reflect.Indirect(val)
}
// should double check we now have a struct (could still be anything)
if val.Kind() != reflect.Struct {
log.Fatal("unexpected type")
}
// now we grab our values as before (note: I assume table name should come from the struct type)
structType := val.Type()
tableName := structType.Name()
params = append(params, tableName)
for i:=0; i < structType.NumField(); i++ {
field := structType.Field(i)
tag := field.Tag
fieldName := field.Name
fieldType := tag.Get("sql_type")
fieldTags := tag.Get("sql_tag")
paramstring := fieldName + " " + fieldType + " " + fieldTags
params = append(params, paramstring)
}
return params
}
Remove & and Elem(), As follow:
func (db *DB) Migrate(domain ...interface{}) {
// statement := "CREATE TABLE IF NOT EXISTS %s (%s, %s, %s, %s, %s)"
for _,i := range domain {
params := BindStruct(i)
statement := CreateStatement("create", len(params))
_,err := db.Exec(fmt.Sprintf(statement, params...))
if err != nil {
log.Fatal("Error migrating database schema - ", err)
break
}
}
}
func BindStruct(domain interface{}) (params []interface{}) {
tableName := reflect.TypeOf(domain).Name()
params = append(params, tableName)
val := reflect.ValueOf(domain)
for i:=0; i < val.NumField(); i++ {
field := val.Type().Field(i)
tag := field.Tag
fieldName := field.Name
fieldType := tag.Get("sql_type")
fieldTags := tag.Get("sql_tag")
paramstring := fieldName + " " + fieldType + " " + fieldTags
params = append(params, paramstring)
}
return params
}