From 971ab11ee2477cf81f7b7db520bb5c151440d298 Mon Sep 17 00:00:00 2001 From: Jes Cok Date: Mon, 21 Oct 2024 04:42:51 +0000 Subject: [PATCH] database/sql: rewrite Null[T].Value method, update doc for Null[T] Update doc for Null[T] to clarify that T should be one of the types accepted by driver.Value. Modify the Value() method of Null[T]: 1) recognize T implementing driver.Valuer interface and invoke it. 2) use the DefaultParameterConverter to convert native types that are not directly supported as driver.Value types. Fixes #69728 Fixes #69837 Change-Id: Iba782c878b2bde168125f5390abf319b88424149 GitHub-Last-Rev: 3df182d23dd57bd04828c3e9bd0c5222d8bef152 GitHub-Pull-Request: golang/go#69938 Reviewed-on: https://go-review.googlesource.com/c/go/+/620858 Auto-Submit: Ian Lance Taylor LUCI-TryBot-Result: Go LUCI Reviewed-by: Michael Pratt Reviewed-by: Ian Lance Taylor --- src/database/sql/sql.go | 14 ++++++++++- src/database/sql/sql_test.go | 47 ++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go index b7cf3d12fa..eddb647ed0 100644 --- a/src/database/sql/sql.go +++ b/src/database/sql/sql.go @@ -410,6 +410,8 @@ func (n NullTime) Value() (driver.Value, error) { // } else { // // NULL value // } +// +// T should be one of the types accepted by [driver.Value]. type Null[T any] struct { V T Valid bool @@ -428,7 +430,17 @@ func (n Null[T]) Value() (driver.Value, error) { if !n.Valid { return nil, nil } - return n.V, nil + v := any(n.V) + // See issue 69728. + if valuer, ok := v.(driver.Valuer); ok { + val, err := callValuerValue(valuer) + if err != nil { + return val, err + } + v = val + } + // See issue 69837. + return driver.DefaultParameterConverter.ConvertValue(v) } // Scanner is an interface used by [Rows.Scan]. diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go index 110a2bae5b..db1d8b3c6b 100644 --- a/src/database/sql/sql_test.go +++ b/src/database/sql/sql_test.go @@ -4957,3 +4957,50 @@ func BenchmarkConnRequestSet(b *testing.B) { } } } + +func TestIssue69837(t *testing.T) { + u := Null[uint]{V: 1, Valid: true} + val, err := driver.DefaultParameterConverter.ConvertValue(u) + if err != nil { + t.Errorf("ConvertValue() error = %v, want nil", err) + } + + if v, ok := val.(int64); !ok { + t.Errorf("val.(type): got %T, expected int64", val) + } else if v != 1 { + t.Errorf("val: got %d, expected 1", v) + } +} + +type issue69728Type struct { + ID int + Name string +} + +func (t issue69728Type) Value() (driver.Value, error) { + return []byte(fmt.Sprintf("%d, %s", t.ID, t.Name)), nil +} + +func TestIssue69728(t *testing.T) { + forValue := Null[issue69728Type]{ + Valid: true, + V: issue69728Type{ + ID: 42, + Name: "foobar", + }, + } + + v1, err := forValue.Value() + if err != nil { + t.Errorf("forValue.Value() error = %v, want nil", err) + } + + v2, err := forValue.V.Value() + if err != nil { + t.Errorf("forValue.V.Value() error = %v, want nil", err) + } + + if !reflect.DeepEqual(v1, v2) { + t.Errorf("not equal; v1 = %v, v2 = %v", v1, v2) + } +} -- 2.48.1