Description
Problem Description:
We are encountering an issue when parsing a nullable typeid
from the database and then using it in another query. When we parse a NULL value, we expect to get NULL back, but instead, we get prefix_00000000000000000000000000
, which disrupts our query.
Currently, scanning nils/empty strings results in a zero-initialized typeid
:
Lines 13 to 18 in dd4ff4b
However, when encoding it back, we get prefix_00000000000000000000000000
, which is neither NULL nor an empty string:
Lines 31 to 33 in dd4ff4b
The discussion in #11 mainly focuses on parsing but does not address putting the ID back into the database.
Question:
How should we handle NULL values?
Option 1: Wrapping typeid
in a Parent Struct to Handle NULL Explicitly
Example wrapping code:
type NullableTypeID[P typeid.PrefixType] struct {
Valid bool
TypeID typeid.TypeID[P]
}
func (ntid *NullableTypeID[P]) Scan(src any) error {
switch obj := src.(type) {
case nil:
return nil
case string:
if err := ntid.TypeID.Scan(src); err != nil {
return err
}
ntid.Valid = true
return nil
default:
return fmt.Errorf("unsupported scan type %T", obj)
}
}
func (ntid NullableTypeID[P]) Value() (driver.Value, error) {
if !ntid.Valid {
return nil, nil
}
return ntid.TypeID.String(), nil
}
This approach has some disadvantages like the fact that NullableTypeID.TypeID is not comparable to a regular TypeID.
In this case, I would either like to get some documentation that recommends this approach or even include the wrapper in the package directly.
Option 2: typeid.Value()
Returns NULL Based on isZero
The Value()
method is updated to:
func (tid TypeID[P]) Value() (driver.Value, error) {
if tid.IsZero() {
return nil, nil
}
return tid.String(), nil
}
This could cause an issue if someone was relying on ""
being converted to "prefix_00000000000000000000000000"
if the column wasn't nullable.
Just an extra note:
From the description of sql.Scanner
the current Scan()
implementation is invalid as we are losing the distinction between NULL and ""
.
// An error should be returned if the value cannot be stored
// without loss of information.