database/sql: sql.Null[T] allows types incompatible with driver.Value, leading to runtime errors
Go version
go1.23.1
Output of go env in your module/workspace:
Not relevant
What did you do?
See example
What did you see happen?
Additional Context:
The defaultConverter in Go's database/sql package already includes logic for converting some native types that are not directly supported as driver.Value types. For example:
- Unsigned integers (uint, uint8, uint16, uint32) are converted to int64.
- uint64 values are converted to int64 if they fit within the int64 range; otherwise, an error is returned.
- Pointers are dereferenced recursively, and the underlying value is converted.
- Floating-point numbers like float32 are converted to float64.
This conversion logic allows these types to be used with SQL drivers indirectly, even though they are not directly accepted as driver.Value.
However, when using sql.Null[T], if T is an unsupported type like uint, it results in the error non-Value type %T returned from Value because sql.Null[T].Value() does not automatically apply the same conversion logic.
What did you expect to see?
- Documentation Update: Clarify in the sql.Null[T] documentation that T should be one of the types accepted by driver.Value.
- Type Constraints: Introduce type constraints to sql.Null[T] to restrict T to acceptable types.
- Type Conversion in Value(): Modify the Value() method of sql.Null[T] to use the DefaultParameterConverter in V
Related Issues and Documentation
- database/sql: Inconsistent Behavior with Custom Null Types #66621 (closed)
- database/sql: sql.Null[T].Value method does not recognize T implementing driver.Valuer interface itself #69728
- database/sql: panic inserting a nil pointer when underlying type implements driver.Valuer #8415 (closed)
- database/sql: the Null* types naively/prematurely set .Valid=true before converting and scanning data #45662
- database/sql: check Valuer pointer types if nil to insert NULL
- database/sql: convert null values to []byte as nil #2788 (closed)
- database/sql/driver: update documentation on what types drivers can return from Scan calls #14621 (closed)
- database/sql: accept nil pointers to Valuers implemented on value receivers
- database/sql: add configuration option to ignore null values in Scan #57066
- Proposal: database/sql: NULL column values should be parsed as zero values for floats and ints instead of trying to parse with strconv #22542 (closed)
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
cc @bradfitz @kardianos @kevinburke
this problem is also happened for me here's my example: https://go.dev/play/p/j8jGELERtNb
as you can see myType's underlying type is string (one of driver.Value accepted types) but still can not be converted
Change https://go.dev/cl/620858 mentions this issue: database/sql: refactor Null[T].Value method, update doc for Null[T]
CC @methane
The documentation for driver.Value states:
Value is a value that drivers must be able to handle. It is either nil, a type handled by a database driver's NamedValueChecker interface, or an instance of one of these types: [...]
This change makes a sql.Null[uint64] outright invalid (if it overflows) with no option for the driver to chime in.
If Value is allowed to be a type handled by a specific driver then shouldn't sql.Null[T] be valid for any T the driver accepts?
This is based on the assumption that the point of sql.Null[T] is to be a general implementation, this change severely constraints its usefulness compared with existing sql.NullXXX types.