var (
from struct {
S sql.NullString
T sql.NullTime
}
to struct {
S *string
T *time.Time
}
)
err := copier.Copy(&to, from)
if err != nil {
fmt.Printf("err should be nil but %v\n", err)
}
if to.S != nil {
fmt.Printf("to.S should be nil but %v\n", to.S)
}
if to.T != nil {
fmt.Printf("to.T should be nil but %v\n", to.T)
}
Actual output:
to.S should be nil but 0xc000014350
to.T should be nil but 0001-01-01 00:00:00 +0000 UTC
Description
When a sql.Null* field (or other driver.Valuer types) is copied to a pointer type field (e.g. from sql.NullString to *string), the destination is always initialized to the zero value of the pointed type (e.g. a pointer to empty string), even if sql.Null* field is invalid (which represent NULL value in sql).
It is expected that invalid sql.Null* value should be copied to the pointer type destination field as nil.
The bug occurred in the following fragment:
// process for nested anonymous field
destFieldNotSet := false
if f, ok := dest.Type().FieldByName(destFieldName); ok {
for idx := range f.Index {
destField := dest.FieldByIndex(f.Index[:idx+1])
if destField.Kind() != reflect.Ptr {
continue
}
if !destField.IsNil() {
continue
}
if !destField.CanSet() {
destFieldNotSet = true
break
}
// destField is a nil pointer that can be set
newValue := reflect.New(destField.Type().Elem())
destField.Set(newValue)
}
}
The above fragment attempted to initialize the struct for embedded fields, but it resulted in all pointer field to be initialised to its zero value even if it is not an embedded struct.
Reproducible Example
https://go.dev/play/p/FBJR9wgiYgf
Actual output:
Description
When a
sql.Null*
field (or other driver.Valuer types) is copied to a pointer type field (e.g. fromsql.NullString
to*string
), the destination is always initialized to the zero value of the pointed type (e.g. a pointer to empty string), even ifsql.Null*
field is invalid (which represent NULL value in sql).It is expected that invalid
sql.Null*
value should be copied to the pointer type destination field as nil.The bug occurred in the following fragment:
The above fragment attempted to initialize the struct for embedded fields, but it resulted in all pointer field to be initialised to its zero value even if it is not an embedded struct.