diff --git a/.gitignore b/.gitignore index 3aa3a0a..7b9b3e7 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ go.work cover.out cover.html .vscode +.idea \ No newline at end of file diff --git a/option.go b/option.go index 9955094..5212556 100644 --- a/option.go +++ b/option.go @@ -297,6 +297,16 @@ func (o *Option[T]) Scan(src any) error { } } + // we try to convertAssign values that we can't directly assign because ConvertValue + // will return immediately for v that is already a Value, even if it is a different + // Value type than the one we expect here. + var st sql.Null[T] + if err := st.Scan(src); err == nil { + o.isPresent = true + o.value = st.V + return nil + } + return fmt.Errorf("failed to scan Option[T]") } @@ -306,7 +316,7 @@ func (o Option[T]) Value() (driver.Value, error) { return nil, nil } - return o.value, nil + return driver.DefaultParameterConverter.ConvertValue(o.value) } // leftValue returns an error if the Option is None, otherwise nil diff --git a/option_test.go b/option_test.go index 55835ea..0969f8b 100644 --- a/option_test.go +++ b/option_test.go @@ -407,6 +407,25 @@ func TestOptionScan(t *testing.T) { is.Equal(err2Exp, err2) } +func TestOptionScanWithPossibleConvert(t *testing.T) { + is := assert.New(t) + + // As passed by the sql package in some cases, src is a []byte. + // https://github.com/golang/go/blob/071b8d51c1a70fa6b12f0bed2e93370e193333fd/src/database/sql/convert.go#L396 + src1 := []byte{65, 66, 67} + dest1 := None[string]() + src2 := int32(32) + dest2 := None[int]() + + err1 := dest1.Scan(src1) + err2 := dest2.Scan(src2) + + is.Nil(err1) + is.Equal(Some("ABC"), dest1) + is.Nil(err2) + is.Equal(Some(32), dest2) +} + func TestOptionValue(t *testing.T) { is := assert.New(t) @@ -425,6 +444,17 @@ func TestOptionValue(t *testing.T) { is.Nil(err2) } +func TestOptionValueWithPossibleConvert(t *testing.T) { + is := assert.New(t) + + opt := Some(uint32(42)) + expected := int64(42) + + value, err := opt.Value() + is.Nil(err) + is.Equal(expected, value) +} + type SomeScanner struct { Cool bool Some int