这是indexloc提供的服务,不要输入任何密码
Skip to content

spanner: DecodeSpanner is not called when using Row.ToStruct() and the target field is *T #12495

@0daryo

Description

@0daryo

Client

Spanner

Environment

macOS (Apple Silicon)
Go 1.24.0
Using Row.ToStruct() with custom types

Code and Dependencies

package main

import (
	"context"
	"fmt"
	"log"

	"cloud.google.com/go/spanner"
)

type A struct {
	B *B
}

type B struct {
	Val string
}

func (b *B) DecodeSpanner(input interface{}) error {
	log.Println("DecodeSpanner called") // ← never called
	if s, ok := input.(string); ok {
		b.Val = "decoded:" + s
		return nil
	}
	return fmt.Errorf("unexpected type: %T", input)
}

func main() {
	row := spanner.RowFromSlice([]any{"Val"}, []any{"foo"})

	var a A
	if err := row.ToStruct(&a); err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%+v\n", a)
}

Expected behavior

When using Row.ToStruct(), and the destination struct has a field of type *T where *T implements spanner.Decoder, DecodeSpanner() should be invoked and the field should be populated accordingly.
Pointer fields are commonly used in golang projects

Actual behavior

The custom DecodeSpanner() method is not called when the destination field is a pointer.
This appears to be because a **T value is passed down during decoding, which fails the spanner.Decoder interface check.
As a result, the pointer remains nil.

if err := decodeValue(pb.Values[i], f.Type, v.FieldByIndex(sf.Index).Addr().Interface(), opts...); err != nil {

Screenshots

n/a

Additional context

Related internal code:

if err := decodeValue(pb.Values[i], f.Type, v.FieldByIndex(sf.Index).Addr().Interface(), opts...); err != nil {

Workarounds considered:

Changing *B to B → loses nullability

Manual decoding with GenericColumnValue → verbose and breaks struct mapping

Please let me know if there's a recommended idiomatic pattern for this case.

Metadata

Metadata

Assignees

Labels

api: spannerIssues related to the Spanner API.triage meI really want to be triaged.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions