Skip to content

proposal: database/sql: convertAssign should be exposed as DefaultConvertAssign #24258

@awreece

Description

@awreece

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions