-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
It would be nice for there to be a generic convertAssign
method exposed a la DefaultParameterConvert
.
For an example use case, I want to make a custom wrapper for driver.Valuer
and sql.Scanner
that will store in the database null
when my object is the zero value (this allows me to use the "zero value means empty" idiom in proto3 and "null means empty" in sql).
For the driver.Valuer
side, this is simple: I check to see if the value is zero, and otherwise delegate to DefaultParameterConverter
:
type protoFieldWrapper reflect.Value
//
// Value implements the database/sql/driver.Valuer interface for the wrapped value,
// returning null if the value is the zero value or a driver.Value otherwise.
//
func (pw protoFieldWrapper) Value() (driver.Value, error) {
pwv := reflect.Value(pw)
if reflect.DeepEqual(pwv.Interface(), reflect.Zero(pwv.Type()).Interface()) {
return nil, nil
}
v := pwv.Interface()
return driver.DefaultParameterConverter.ConvertValue(v)
}
Unfortunately, convertAssign
is not exposed, making the corresponding in Scan much harder:
//
// Scan implements the database/sql.Scanner interface for the wrapped value,
// setting the value to its zero value if the src is null or scanning it otherwise.
//
func (pw protoFieldWrapper) Scan(src interface{}) error {
pwv := reflect.Value(pw)
if src == nil {
pwv.Set(reflect.Zero(pwv.Type()))
return nil
}
if !driver.IsValue(src) {
return errors.Errorf("don't know how to scan %T", src)
}
t := reflect.TypeOf(src)
if !t.ConvertibleTo(pwv.Type()) {
return errors.Errorf("could not assign value of type %v to %v", t, pwv.Type())
}
pwv.Set(reflect.ValueOf(src).Convert(pwv.Type()))
return nil
}
A savvy reader will note that the above code is not actually as robust as convertAssign
in a myriad of ways:
- If the value being scanned is a pointer,
convertAssign
will allocate a new object and scan into it, if possible. - If the value being scanned is a
sql.Scanner
,convertAssign
will call.Scan()
. - etc.
#22544 is a related proposal, but it appears to be focused on performance/safety for driver, not improving the end user api here.