Open egonelbre opened 1 day ago
Looks like upstream code does work:
client := must(spanner.NewClient(ctx, "projects/"+ProjectID+"/instances/"+InstanceID+"/databases/"+DatabaseName))
defer client.Close()
type Entry struct {
Name string
Value int64
}
input := []Entry{
{Name: "Hello", Value: 1000},
{Name: "World", Value: 2000},
}
rows := client.ReadOnlyTransaction().Query(ctx, spanner.Statement{
SQL: `SELECT ARRAY(SELECT AS STRUCT Name, Value FROM UNNEST(@input)) AS entries`,
Params: map[string]any{
"input": input,
},
})
for {
row, err := rows.Next()
if err != nil {
fmt.Fprintln(os.Stderr, err)
break
}
var output []*Entry // NOTE: []Entry does not work
if err := row.Columns(&output); err != nil {
fmt.Fprintln(os.Stderr, err)
}
for _, entry := range output {
fmt.Println(entry)
}
}
It does look like that supporting that behavior is difficult with the current database/sql
implementation. https://github.com/golang/go/issues/67546 would probably allow to fix that.
An alternative would be to instead pass spanner.GenericColumnValue
back to the user, which would allow at least to get the values out.
var generic spanner.GenericColumnValue
if err := rows.Scan(&generic); err != nil {
return fmt.Errorf("failed to scan row values: %v", err)
}
var allEntries []*Entry
err := generic.Decode(&allEntries)
The https://github.com/googleapis/go-sql-spanner/blob/c39f57f06cbccd7cdde1091c844a916fddc3d67b/rows.go#L268 default cases would then read:
default:
dest[i] = col
}
default:
dest[i] = col
}
The drawback for this approach is that when you use GenericColumnValue
, then when adding more specific implementations then they might break; because they won't be able to scan into it anymore. However, since the database/sql
feature seems to be on track for 1.24, it can be fixed then.
Scanning array of spanner structs into a slice of Go structs doesn't seem to be working. Smallest reproducer:
This fails with:
If the code is changed to use
TO_JSON
andspanner.NullJSON
it is able to return the correct values.It's unclear whether the issue is in go-sql-spanner or upstream.