From 78fb05dedbb13e1fce54accc795d49c1d7425f2e Mon Sep 17 00:00:00 2001 From: Egon Elbre Date: Thu, 29 Aug 2024 15:35:37 +0300 Subject: [PATCH] fix: driver.Valuer method was being ignored (#289) When a type had implemented custom Value func to convert the type, then the conversion method was not being called for structs. Fixes #281 --- driver.go | 12 ++++++++---- driver_test.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/driver.go b/driver.go index 9f63b424..75deb480 100644 --- a/driver.go +++ b/driver.go @@ -748,13 +748,11 @@ func (c *conn) IsValid() bool { return !c.closed } +// checkIsValidType returns true for types that do not need extra conversion +// for spanner. func checkIsValidType(v driver.Value) bool { switch v.(type) { default: - // google-cloud-go/spanner knows how to deal with these - if isStructOrArrayOfStructValue(v) || isAnArrayOfProtoColumn(v) { - return true - } return false case nil: case sql.NullInt64: @@ -851,6 +849,12 @@ func (c *conn) CheckNamedValue(value *driver.NamedValue) error { return nil } } + + // google-cloud-go/spanner knows how to deal with these + if isStructOrArrayOfStructValue(value.Value) || isAnArrayOfProtoColumn(value.Value) { + return nil + } + return spanner.ToSpannerError(status.Errorf(codes.InvalidArgument, "unsupported value type: %T", value.Value)) } diff --git a/driver_test.go b/driver_test.go index aab507bc..5c38263c 100644 --- a/driver_test.go +++ b/driver_test.go @@ -467,3 +467,52 @@ func TestConn_GetCommitTimestampAfterAutocommitQuery(t *testing.T) { t.Fatalf("error code mismatch\n Got: %v\nWant: %v", g, w) } } + +func TestConn_CheckNamedValue(t *testing.T) { + c := &conn{} + + type Person struct { + Name string + Age int64 + } + + tests := []struct { + in any + want any + }{ + // basic types should stay unchanged + {in: int64(256), want: int64(256)}, + // driver.Valuer types should call .Value func + {in: &ValuerPerson{Name: "hello", Age: 123}, want: "hello"}, + // structs should be sent to spanner + {in: &Person{Name: "hello", Age: 123}, want: &Person{Name: "hello", Age: 123}}, + } + + for _, test := range tests { + value := &driver.NamedValue{ + Ordinal: 1, + Value: test.in, + } + + err := c.CheckNamedValue(value) + if err != nil { + t.Errorf("expected no error, got: %v", err) + continue + } + + if diff := cmp.Diff(test.want, value.Value); diff != "" { + t.Errorf("wrong result, got %#v expected %#v:\n%v", value.Value, test.want, diff) + } + } +} + +// ValuerPerson implements driver.Valuer +type ValuerPerson struct { + Name string + Age int64 +} + +// Value implements conversion func for database. +func (p *ValuerPerson) Value() (driver.Value, error) { + return p.Name, nil +}