2709 lines
70 KiB
Go
2709 lines
70 KiB
Go
/*
|
|
Copyright 2017 Google LLC
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package spanner
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"math"
|
|
"reflect"
|
|
"strconv"
|
|
"time"
|
|
|
|
"cloud.google.com/go/civil"
|
|
"cloud.google.com/go/internal/fields"
|
|
"github.com/golang/protobuf/proto"
|
|
proto3 "github.com/golang/protobuf/ptypes/struct"
|
|
sppb "google.golang.org/genproto/googleapis/spanner/v1"
|
|
"google.golang.org/grpc/codes"
|
|
)
|
|
|
|
// nullString is returned by the String methods of NullableValues when the
|
|
// underlying database value is null.
|
|
const nullString = "<null>"
|
|
const commitTimestampPlaceholderString = "spanner.commit_timestamp()"
|
|
|
|
var (
|
|
// CommitTimestamp is a special value used to tell Cloud Spanner to insert
|
|
// the commit timestamp of the transaction into a column. It can be used in
|
|
// a Mutation, or directly used in InsertStruct or InsertMap. See
|
|
// ExampleCommitTimestamp. This is just a placeholder and the actual value
|
|
// stored in this variable has no meaning.
|
|
CommitTimestamp = commitTimestamp
|
|
commitTimestamp = time.Unix(0, 0).In(time.FixedZone("CommitTimestamp placeholder", 0xDB))
|
|
|
|
jsonNullBytes = []byte("null")
|
|
)
|
|
|
|
// NullableValue is the interface implemented by all null value wrapper types.
|
|
type NullableValue interface {
|
|
// IsNull returns true if the underlying database value is null.
|
|
IsNull() bool
|
|
}
|
|
|
|
// NullInt64 represents a Cloud Spanner INT64 that may be NULL.
|
|
type NullInt64 struct {
|
|
Int64 int64
|
|
Valid bool // Valid is true if Int64 is not NULL.
|
|
}
|
|
|
|
// IsNull implements NullableValue.IsNull for NullInt64.
|
|
func (n NullInt64) IsNull() bool {
|
|
return !n.Valid
|
|
}
|
|
|
|
// String implements Stringer.String for NullInt64
|
|
func (n NullInt64) String() string {
|
|
if !n.Valid {
|
|
return nullString
|
|
}
|
|
return fmt.Sprintf("%v", n.Int64)
|
|
}
|
|
|
|
// MarshalJSON implements json.Marshaler.MarshalJSON for NullInt64.
|
|
func (n NullInt64) MarshalJSON() ([]byte, error) {
|
|
if n.Valid {
|
|
return []byte(fmt.Sprintf("%v", n.Int64)), nil
|
|
}
|
|
return jsonNullBytes, nil
|
|
}
|
|
|
|
// UnmarshalJSON implements json.Unmarshaler.UnmarshalJSON for NullInt64.
|
|
func (n *NullInt64) UnmarshalJSON(payload []byte) error {
|
|
if payload == nil {
|
|
return fmt.Errorf("payload should not be nil")
|
|
}
|
|
if bytes.Equal(payload, jsonNullBytes) {
|
|
n.Int64 = int64(0)
|
|
n.Valid = false
|
|
return nil
|
|
}
|
|
num, err := strconv.ParseInt(string(payload), 10, 64)
|
|
if err != nil {
|
|
return fmt.Errorf("payload cannot be converted to int64: got %v", string(payload))
|
|
}
|
|
n.Int64 = num
|
|
n.Valid = true
|
|
return nil
|
|
}
|
|
|
|
// NullString represents a Cloud Spanner STRING that may be NULL.
|
|
type NullString struct {
|
|
StringVal string
|
|
Valid bool // Valid is true if StringVal is not NULL.
|
|
}
|
|
|
|
// IsNull implements NullableValue.IsNull for NullString.
|
|
func (n NullString) IsNull() bool {
|
|
return !n.Valid
|
|
}
|
|
|
|
// String implements Stringer.String for NullString
|
|
func (n NullString) String() string {
|
|
if !n.Valid {
|
|
return nullString
|
|
}
|
|
return n.StringVal
|
|
}
|
|
|
|
// MarshalJSON implements json.Marshaler.MarshalJSON for NullString.
|
|
func (n NullString) MarshalJSON() ([]byte, error) {
|
|
if n.Valid {
|
|
return []byte(fmt.Sprintf("%q", n.StringVal)), nil
|
|
}
|
|
return jsonNullBytes, nil
|
|
}
|
|
|
|
// UnmarshalJSON implements json.Unmarshaler.UnmarshalJSON for NullString.
|
|
func (n *NullString) UnmarshalJSON(payload []byte) error {
|
|
if payload == nil {
|
|
return fmt.Errorf("payload should not be nil")
|
|
}
|
|
if bytes.Equal(payload, jsonNullBytes) {
|
|
n.StringVal = ""
|
|
n.Valid = false
|
|
return nil
|
|
}
|
|
payload, err := trimDoubleQuotes(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
n.StringVal = string(payload)
|
|
n.Valid = true
|
|
return nil
|
|
}
|
|
|
|
// NullFloat64 represents a Cloud Spanner FLOAT64 that may be NULL.
|
|
type NullFloat64 struct {
|
|
Float64 float64
|
|
Valid bool // Valid is true if Float64 is not NULL.
|
|
}
|
|
|
|
// IsNull implements NullableValue.IsNull for NullFloat64.
|
|
func (n NullFloat64) IsNull() bool {
|
|
return !n.Valid
|
|
}
|
|
|
|
// String implements Stringer.String for NullFloat64
|
|
func (n NullFloat64) String() string {
|
|
if !n.Valid {
|
|
return nullString
|
|
}
|
|
return fmt.Sprintf("%v", n.Float64)
|
|
}
|
|
|
|
// MarshalJSON implements json.Marshaler.MarshalJSON for NullFloat64.
|
|
func (n NullFloat64) MarshalJSON() ([]byte, error) {
|
|
if n.Valid {
|
|
return []byte(fmt.Sprintf("%v", n.Float64)), nil
|
|
}
|
|
return jsonNullBytes, nil
|
|
}
|
|
|
|
// UnmarshalJSON implements json.Unmarshaler.UnmarshalJSON for NullFloat64.
|
|
func (n *NullFloat64) UnmarshalJSON(payload []byte) error {
|
|
if payload == nil {
|
|
return fmt.Errorf("payload should not be nil")
|
|
}
|
|
if bytes.Equal(payload, jsonNullBytes) {
|
|
n.Float64 = float64(0)
|
|
n.Valid = false
|
|
return nil
|
|
}
|
|
num, err := strconv.ParseFloat(string(payload), 64)
|
|
if err != nil {
|
|
return fmt.Errorf("payload cannot be converted to float64: got %v", string(payload))
|
|
}
|
|
n.Float64 = num
|
|
n.Valid = true
|
|
return nil
|
|
}
|
|
|
|
// NullBool represents a Cloud Spanner BOOL that may be NULL.
|
|
type NullBool struct {
|
|
Bool bool
|
|
Valid bool // Valid is true if Bool is not NULL.
|
|
}
|
|
|
|
// IsNull implements NullableValue.IsNull for NullBool.
|
|
func (n NullBool) IsNull() bool {
|
|
return !n.Valid
|
|
}
|
|
|
|
// String implements Stringer.String for NullBool
|
|
func (n NullBool) String() string {
|
|
if !n.Valid {
|
|
return nullString
|
|
}
|
|
return fmt.Sprintf("%v", n.Bool)
|
|
}
|
|
|
|
// MarshalJSON implements json.Marshaler.MarshalJSON for NullBool.
|
|
func (n NullBool) MarshalJSON() ([]byte, error) {
|
|
if n.Valid {
|
|
return []byte(fmt.Sprintf("%v", n.Bool)), nil
|
|
}
|
|
return jsonNullBytes, nil
|
|
}
|
|
|
|
// UnmarshalJSON implements json.Unmarshaler.UnmarshalJSON for NullBool.
|
|
func (n *NullBool) UnmarshalJSON(payload []byte) error {
|
|
if payload == nil {
|
|
return fmt.Errorf("payload should not be nil")
|
|
}
|
|
if bytes.Equal(payload, jsonNullBytes) {
|
|
n.Bool = false
|
|
n.Valid = false
|
|
return nil
|
|
}
|
|
b, err := strconv.ParseBool(string(payload))
|
|
if err != nil {
|
|
return fmt.Errorf("payload cannot be converted to bool: got %v", string(payload))
|
|
}
|
|
n.Bool = b
|
|
n.Valid = true
|
|
return nil
|
|
}
|
|
|
|
// NullTime represents a Cloud Spanner TIMESTAMP that may be null.
|
|
type NullTime struct {
|
|
Time time.Time
|
|
Valid bool // Valid is true if Time is not NULL.
|
|
}
|
|
|
|
// IsNull implements NullableValue.IsNull for NullTime.
|
|
func (n NullTime) IsNull() bool {
|
|
return !n.Valid
|
|
}
|
|
|
|
// String implements Stringer.String for NullTime
|
|
func (n NullTime) String() string {
|
|
if !n.Valid {
|
|
return nullString
|
|
}
|
|
return n.Time.Format(time.RFC3339Nano)
|
|
}
|
|
|
|
// MarshalJSON implements json.Marshaler.MarshalJSON for NullTime.
|
|
func (n NullTime) MarshalJSON() ([]byte, error) {
|
|
if n.Valid {
|
|
return []byte(fmt.Sprintf("%q", n.String())), nil
|
|
}
|
|
return jsonNullBytes, nil
|
|
}
|
|
|
|
// UnmarshalJSON implements json.Unmarshaler.UnmarshalJSON for NullTime.
|
|
func (n *NullTime) UnmarshalJSON(payload []byte) error {
|
|
if payload == nil {
|
|
return fmt.Errorf("payload should not be nil")
|
|
}
|
|
if bytes.Equal(payload, jsonNullBytes) {
|
|
n.Time = time.Time{}
|
|
n.Valid = false
|
|
return nil
|
|
}
|
|
payload, err := trimDoubleQuotes(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s := string(payload)
|
|
t, err := time.Parse(time.RFC3339Nano, s)
|
|
if err != nil {
|
|
return fmt.Errorf("payload cannot be converted to time.Time: got %v", string(payload))
|
|
}
|
|
n.Time = t
|
|
n.Valid = true
|
|
return nil
|
|
}
|
|
|
|
// NullDate represents a Cloud Spanner DATE that may be null.
|
|
type NullDate struct {
|
|
Date civil.Date
|
|
Valid bool // Valid is true if Date is not NULL.
|
|
}
|
|
|
|
// IsNull implements NullableValue.IsNull for NullDate.
|
|
func (n NullDate) IsNull() bool {
|
|
return !n.Valid
|
|
}
|
|
|
|
// String implements Stringer.String for NullDate
|
|
func (n NullDate) String() string {
|
|
if !n.Valid {
|
|
return nullString
|
|
}
|
|
return n.Date.String()
|
|
}
|
|
|
|
// MarshalJSON implements json.Marshaler.MarshalJSON for NullDate.
|
|
func (n NullDate) MarshalJSON() ([]byte, error) {
|
|
if n.Valid {
|
|
return []byte(fmt.Sprintf("%q", n.String())), nil
|
|
}
|
|
return jsonNullBytes, nil
|
|
}
|
|
|
|
// UnmarshalJSON implements json.Unmarshaler.UnmarshalJSON for NullDate.
|
|
func (n *NullDate) UnmarshalJSON(payload []byte) error {
|
|
if payload == nil {
|
|
return fmt.Errorf("payload should not be nil")
|
|
}
|
|
if bytes.Equal(payload, jsonNullBytes) {
|
|
n.Date = civil.Date{}
|
|
n.Valid = false
|
|
return nil
|
|
}
|
|
payload, err := trimDoubleQuotes(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s := string(payload)
|
|
t, err := civil.ParseDate(s)
|
|
if err != nil {
|
|
return fmt.Errorf("payload cannot be converted to civil.Date: got %v", string(payload))
|
|
}
|
|
n.Date = t
|
|
n.Valid = true
|
|
return nil
|
|
}
|
|
|
|
// NullRow represents a Cloud Spanner STRUCT that may be NULL.
|
|
// See also the document for Row.
|
|
// Note that NullRow is not a valid Cloud Spanner column Type.
|
|
type NullRow struct {
|
|
Row Row
|
|
Valid bool // Valid is true if Row is not NULL.
|
|
}
|
|
|
|
// GenericColumnValue represents the generic encoded value and type of the
|
|
// column. See google.spanner.v1.ResultSet proto for details. This can be
|
|
// useful for proxying query results when the result types are not known in
|
|
// advance.
|
|
//
|
|
// If you populate a GenericColumnValue from a row using Row.Column or related
|
|
// methods, do not modify the contents of Type and Value.
|
|
type GenericColumnValue struct {
|
|
Type *sppb.Type
|
|
Value *proto3.Value
|
|
}
|
|
|
|
// Decode decodes a GenericColumnValue. The ptr argument should be a pointer
|
|
// to a Go value that can accept v.
|
|
func (v GenericColumnValue) Decode(ptr interface{}) error {
|
|
return decodeValue(v.Value, v.Type, ptr)
|
|
}
|
|
|
|
// NewGenericColumnValue creates a GenericColumnValue from Go value that is
|
|
// valid for Cloud Spanner.
|
|
func newGenericColumnValue(v interface{}) (*GenericColumnValue, error) {
|
|
value, typ, err := encodeValue(v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &GenericColumnValue{Value: value, Type: typ}, nil
|
|
}
|
|
|
|
// errTypeMismatch returns error for destination not having a compatible type
|
|
// with source Cloud Spanner type.
|
|
func errTypeMismatch(srcCode, elCode sppb.TypeCode, dst interface{}) error {
|
|
s := srcCode.String()
|
|
if srcCode == sppb.TypeCode_ARRAY {
|
|
s = fmt.Sprintf("%v[%v]", srcCode, elCode)
|
|
}
|
|
return spannerErrorf(codes.InvalidArgument, "type %T cannot be used for decoding %s", dst, s)
|
|
}
|
|
|
|
// errNilSpannerType returns error for nil Cloud Spanner type in decoding.
|
|
func errNilSpannerType() error {
|
|
return spannerErrorf(codes.FailedPrecondition, "unexpected nil Cloud Spanner data type in decoding")
|
|
}
|
|
|
|
// errNilSrc returns error for decoding from nil proto value.
|
|
func errNilSrc() error {
|
|
return spannerErrorf(codes.FailedPrecondition, "unexpected nil Cloud Spanner value in decoding")
|
|
}
|
|
|
|
// errNilDst returns error for decoding into nil interface{}.
|
|
func errNilDst(dst interface{}) error {
|
|
return spannerErrorf(codes.InvalidArgument, "cannot decode into nil type %T", dst)
|
|
}
|
|
|
|
// errNilArrElemType returns error for input Cloud Spanner data type being a array but without a
|
|
// non-nil array element type.
|
|
func errNilArrElemType(t *sppb.Type) error {
|
|
return spannerErrorf(codes.FailedPrecondition, "array type %v is with nil array element type", t)
|
|
}
|
|
|
|
func errUnsupportedEmbeddedStructFields(fname string) error {
|
|
return spannerErrorf(codes.InvalidArgument, "Embedded field: %s. Embedded and anonymous fields are not allowed "+
|
|
"when converting Go structs to Cloud Spanner STRUCT values. To create a STRUCT value with an "+
|
|
"unnamed field, use a `spanner:\"\"` field tag.", fname)
|
|
}
|
|
|
|
// errDstNotForNull returns error for decoding a SQL NULL value into a destination which doesn't
|
|
// support NULL values.
|
|
func errDstNotForNull(dst interface{}) error {
|
|
return spannerErrorf(codes.InvalidArgument, "destination %T cannot support NULL SQL values", dst)
|
|
}
|
|
|
|
// errBadEncoding returns error for decoding wrongly encoded types.
|
|
func errBadEncoding(v *proto3.Value, err error) error {
|
|
return spannerErrorf(codes.FailedPrecondition, "%v wasn't correctly encoded: <%v>", v, err)
|
|
}
|
|
|
|
func parseNullTime(v *proto3.Value, p *NullTime, code sppb.TypeCode, isNull bool) error {
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if code != sppb.TypeCode_TIMESTAMP {
|
|
return errTypeMismatch(code, sppb.TypeCode_TYPE_CODE_UNSPECIFIED, p)
|
|
}
|
|
if isNull {
|
|
*p = NullTime{}
|
|
return nil
|
|
}
|
|
x, err := getStringValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := time.Parse(time.RFC3339Nano, x)
|
|
if err != nil {
|
|
return errBadEncoding(v, err)
|
|
}
|
|
p.Valid = true
|
|
p.Time = y
|
|
return nil
|
|
}
|
|
|
|
// decodeValue decodes a protobuf Value into a pointer to a Go value, as
|
|
// specified by sppb.Type.
|
|
func decodeValue(v *proto3.Value, t *sppb.Type, ptr interface{}) error {
|
|
if v == nil {
|
|
return errNilSrc()
|
|
}
|
|
if t == nil {
|
|
return errNilSpannerType()
|
|
}
|
|
code := t.Code
|
|
acode := sppb.TypeCode_TYPE_CODE_UNSPECIFIED
|
|
if code == sppb.TypeCode_ARRAY {
|
|
if t.ArrayElementType == nil {
|
|
return errNilArrElemType(t)
|
|
}
|
|
acode = t.ArrayElementType.Code
|
|
}
|
|
_, isNull := v.Kind.(*proto3.Value_NullValue)
|
|
|
|
// Do the decoding based on the type of ptr.
|
|
switch p := ptr.(type) {
|
|
case nil:
|
|
return errNilDst(nil)
|
|
case *string:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if code != sppb.TypeCode_STRING {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
return errDstNotForNull(ptr)
|
|
}
|
|
x, err := getStringValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*p = x
|
|
case *NullString, **string:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if code != sppb.TypeCode_STRING {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
switch sp := ptr.(type) {
|
|
case *NullString:
|
|
*sp = NullString{}
|
|
case **string:
|
|
*sp = nil
|
|
}
|
|
break
|
|
}
|
|
x, err := getStringValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch sp := ptr.(type) {
|
|
case *NullString:
|
|
sp.Valid = true
|
|
sp.StringVal = x
|
|
case **string:
|
|
*sp = &x
|
|
}
|
|
case *[]NullString, *[]*string:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if acode != sppb.TypeCode_STRING {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
switch sp := ptr.(type) {
|
|
case *[]NullString:
|
|
*sp = nil
|
|
case *[]*string:
|
|
*sp = nil
|
|
}
|
|
break
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch sp := ptr.(type) {
|
|
case *[]NullString:
|
|
y, err := decodeNullStringArray(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*sp = y
|
|
case *[]*string:
|
|
y, err := decodeStringPointerArray(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*sp = y
|
|
}
|
|
case *[]string:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if acode != sppb.TypeCode_STRING {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
*p = nil
|
|
break
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := decodeStringArray(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*p = y
|
|
case *[]byte:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if code != sppb.TypeCode_BYTES {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
*p = nil
|
|
break
|
|
}
|
|
x, err := getStringValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := base64.StdEncoding.DecodeString(x)
|
|
if err != nil {
|
|
return errBadEncoding(v, err)
|
|
}
|
|
*p = y
|
|
case *[][]byte:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if acode != sppb.TypeCode_BYTES {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
*p = nil
|
|
break
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := decodeByteArray(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*p = y
|
|
case *int64:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if code != sppb.TypeCode_INT64 {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
return errDstNotForNull(ptr)
|
|
}
|
|
x, err := getStringValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := strconv.ParseInt(x, 10, 64)
|
|
if err != nil {
|
|
return errBadEncoding(v, err)
|
|
}
|
|
*p = y
|
|
case *NullInt64, **int64:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if code != sppb.TypeCode_INT64 {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
switch sp := ptr.(type) {
|
|
case *NullInt64:
|
|
*sp = NullInt64{}
|
|
case **int64:
|
|
*sp = nil
|
|
}
|
|
break
|
|
}
|
|
x, err := getStringValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := strconv.ParseInt(x, 10, 64)
|
|
if err != nil {
|
|
return errBadEncoding(v, err)
|
|
}
|
|
switch sp := ptr.(type) {
|
|
case *NullInt64:
|
|
sp.Valid = true
|
|
sp.Int64 = y
|
|
case **int64:
|
|
*sp = &y
|
|
}
|
|
case *[]NullInt64, *[]*int64:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if acode != sppb.TypeCode_INT64 {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
switch sp := ptr.(type) {
|
|
case *[]NullInt64:
|
|
*sp = nil
|
|
case *[]*int64:
|
|
*sp = nil
|
|
}
|
|
break
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch sp := ptr.(type) {
|
|
case *[]NullInt64:
|
|
y, err := decodeNullInt64Array(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*sp = y
|
|
case *[]*int64:
|
|
y, err := decodeInt64PointerArray(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*sp = y
|
|
}
|
|
case *[]int64:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if acode != sppb.TypeCode_INT64 {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
*p = nil
|
|
break
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := decodeInt64Array(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*p = y
|
|
case *bool:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if code != sppb.TypeCode_BOOL {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
return errDstNotForNull(ptr)
|
|
}
|
|
x, err := getBoolValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*p = x
|
|
case *NullBool, **bool:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if code != sppb.TypeCode_BOOL {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
switch sp := ptr.(type) {
|
|
case *NullBool:
|
|
*sp = NullBool{}
|
|
case **bool:
|
|
*sp = nil
|
|
}
|
|
break
|
|
}
|
|
x, err := getBoolValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch sp := ptr.(type) {
|
|
case *NullBool:
|
|
sp.Valid = true
|
|
sp.Bool = x
|
|
case **bool:
|
|
*sp = &x
|
|
}
|
|
case *[]NullBool, *[]*bool:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if acode != sppb.TypeCode_BOOL {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
switch sp := ptr.(type) {
|
|
case *[]NullBool:
|
|
*sp = nil
|
|
case *[]*bool:
|
|
*sp = nil
|
|
}
|
|
break
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch sp := ptr.(type) {
|
|
case *[]NullBool:
|
|
y, err := decodeNullBoolArray(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*sp = y
|
|
case *[]*bool:
|
|
y, err := decodeBoolPointerArray(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*sp = y
|
|
}
|
|
case *[]bool:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if acode != sppb.TypeCode_BOOL {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
*p = nil
|
|
break
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := decodeBoolArray(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*p = y
|
|
case *float64:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if code != sppb.TypeCode_FLOAT64 {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
return errDstNotForNull(ptr)
|
|
}
|
|
x, err := getFloat64Value(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*p = x
|
|
case *NullFloat64, **float64:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if code != sppb.TypeCode_FLOAT64 {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
switch sp := ptr.(type) {
|
|
case *NullFloat64:
|
|
*sp = NullFloat64{}
|
|
case **float64:
|
|
*sp = nil
|
|
}
|
|
break
|
|
}
|
|
x, err := getFloat64Value(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch sp := ptr.(type) {
|
|
case *NullFloat64:
|
|
sp.Valid = true
|
|
sp.Float64 = x
|
|
case **float64:
|
|
*sp = &x
|
|
}
|
|
case *[]NullFloat64, *[]*float64:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if acode != sppb.TypeCode_FLOAT64 {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
switch sp := ptr.(type) {
|
|
case *[]NullFloat64:
|
|
*sp = nil
|
|
case *[]*float64:
|
|
*sp = nil
|
|
}
|
|
break
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch sp := ptr.(type) {
|
|
case *[]NullFloat64:
|
|
y, err := decodeNullFloat64Array(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*sp = y
|
|
case *[]*float64:
|
|
y, err := decodeFloat64PointerArray(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*sp = y
|
|
}
|
|
case *[]float64:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if acode != sppb.TypeCode_FLOAT64 {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
*p = nil
|
|
break
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := decodeFloat64Array(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*p = y
|
|
case *time.Time:
|
|
var nt NullTime
|
|
if isNull {
|
|
return errDstNotForNull(ptr)
|
|
}
|
|
err := parseNullTime(v, &nt, code, isNull)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*p = nt.Time
|
|
case *NullTime:
|
|
err := parseNullTime(v, p, code, isNull)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case **time.Time:
|
|
var nt NullTime
|
|
if isNull {
|
|
*p = nil
|
|
break
|
|
}
|
|
err := parseNullTime(v, &nt, code, isNull)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*p = &nt.Time
|
|
case *[]NullTime, *[]*time.Time:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if acode != sppb.TypeCode_TIMESTAMP {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
switch sp := ptr.(type) {
|
|
case *[]NullTime:
|
|
*sp = nil
|
|
case *[]*time.Time:
|
|
*sp = nil
|
|
}
|
|
break
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch sp := ptr.(type) {
|
|
case *[]NullTime:
|
|
y, err := decodeNullTimeArray(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*sp = y
|
|
case *[]*time.Time:
|
|
y, err := decodeTimePointerArray(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*sp = y
|
|
}
|
|
case *[]time.Time:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if acode != sppb.TypeCode_TIMESTAMP {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
*p = nil
|
|
break
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := decodeTimeArray(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*p = y
|
|
case *civil.Date:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if code != sppb.TypeCode_DATE {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
return errDstNotForNull(ptr)
|
|
}
|
|
x, err := getStringValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := civil.ParseDate(x)
|
|
if err != nil {
|
|
return errBadEncoding(v, err)
|
|
}
|
|
*p = y
|
|
case *NullDate, **civil.Date:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if code != sppb.TypeCode_DATE {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
switch sp := ptr.(type) {
|
|
case *NullDate:
|
|
*sp = NullDate{}
|
|
case **civil.Date:
|
|
*sp = nil
|
|
}
|
|
break
|
|
}
|
|
x, err := getStringValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := civil.ParseDate(x)
|
|
if err != nil {
|
|
return errBadEncoding(v, err)
|
|
}
|
|
switch sp := ptr.(type) {
|
|
case *NullDate:
|
|
sp.Valid = true
|
|
sp.Date = y
|
|
case **civil.Date:
|
|
*sp = &y
|
|
}
|
|
case *[]NullDate, *[]*civil.Date:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if acode != sppb.TypeCode_DATE {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
switch sp := ptr.(type) {
|
|
case *[]NullDate:
|
|
*sp = nil
|
|
case *[]*civil.Date:
|
|
*sp = nil
|
|
}
|
|
break
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch sp := ptr.(type) {
|
|
case *[]NullDate:
|
|
y, err := decodeNullDateArray(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*sp = y
|
|
case *[]*civil.Date:
|
|
y, err := decodeDatePointerArray(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*sp = y
|
|
}
|
|
case *[]civil.Date:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if acode != sppb.TypeCode_DATE {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
*p = nil
|
|
break
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := decodeDateArray(x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*p = y
|
|
case *[]NullRow:
|
|
if p == nil {
|
|
return errNilDst(p)
|
|
}
|
|
if acode != sppb.TypeCode_STRUCT {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
*p = nil
|
|
break
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := decodeRowArray(t.ArrayElementType.StructType, x)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*p = y
|
|
case *GenericColumnValue:
|
|
*p = GenericColumnValue{Type: t, Value: v}
|
|
default:
|
|
// Check if the pointer is a variant of a base type.
|
|
decodableType := getDecodableSpannerType(ptr)
|
|
if decodableType != spannerTypeUnknown {
|
|
if isNull && !decodableType.supportsNull() {
|
|
return errDstNotForNull(ptr)
|
|
}
|
|
return decodableType.decodeValueToCustomType(v, t, acode, ptr)
|
|
}
|
|
|
|
// Check if the proto encoding is for an array of structs.
|
|
if !(code == sppb.TypeCode_ARRAY && acode == sppb.TypeCode_STRUCT) {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
vp := reflect.ValueOf(p)
|
|
if !vp.IsValid() {
|
|
return errNilDst(p)
|
|
}
|
|
if !isPtrStructPtrSlice(vp.Type()) {
|
|
// The container is not a pointer to a struct pointer slice.
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
// Only use reflection for nil detection on slow path.
|
|
// Also, IsNil panics on many types, so check it after the type check.
|
|
if vp.IsNil() {
|
|
return errNilDst(p)
|
|
}
|
|
if isNull {
|
|
// The proto Value is encoding NULL, set the pointer to struct
|
|
// slice to nil as well.
|
|
vp.Elem().Set(reflect.Zero(vp.Elem().Type()))
|
|
break
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = decodeStructArray(t.ArrayElementType.StructType, x, p); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// decodableSpannerType represents the Go types that a value from a Spanner
|
|
// database can be converted to.
|
|
type decodableSpannerType uint
|
|
|
|
const (
|
|
spannerTypeUnknown decodableSpannerType = iota
|
|
spannerTypeInvalid
|
|
spannerTypeNonNullString
|
|
spannerTypeByteArray
|
|
spannerTypeNonNullInt64
|
|
spannerTypeNonNullBool
|
|
spannerTypeNonNullFloat64
|
|
spannerTypeNonNullTime
|
|
spannerTypeNonNullDate
|
|
spannerTypeNullString
|
|
spannerTypeNullInt64
|
|
spannerTypeNullBool
|
|
spannerTypeNullFloat64
|
|
spannerTypeNullTime
|
|
spannerTypeNullDate
|
|
spannerTypeArrayOfNonNullString
|
|
spannerTypeArrayOfByteArray
|
|
spannerTypeArrayOfNonNullInt64
|
|
spannerTypeArrayOfNonNullBool
|
|
spannerTypeArrayOfNonNullFloat64
|
|
spannerTypeArrayOfNonNullTime
|
|
spannerTypeArrayOfNonNullDate
|
|
spannerTypeArrayOfNullString
|
|
spannerTypeArrayOfNullInt64
|
|
spannerTypeArrayOfNullBool
|
|
spannerTypeArrayOfNullFloat64
|
|
spannerTypeArrayOfNullTime
|
|
spannerTypeArrayOfNullDate
|
|
)
|
|
|
|
// supportsNull returns true for the Go types that can hold a null value from
|
|
// Spanner.
|
|
func (d decodableSpannerType) supportsNull() bool {
|
|
switch d {
|
|
case spannerTypeNonNullString, spannerTypeNonNullInt64, spannerTypeNonNullBool, spannerTypeNonNullFloat64, spannerTypeNonNullTime, spannerTypeNonNullDate:
|
|
return false
|
|
default:
|
|
return true
|
|
}
|
|
}
|
|
|
|
// The following list of types represent the struct types that represent a
|
|
// specific Spanner data type in Go. If a pointer to one of these types is
|
|
// passed to decodeValue, the client library will decode one column value into
|
|
// the struct. For pointers to all other struct types, the client library will
|
|
// treat it as a generic struct that should contain a field for each column in
|
|
// the result set that is being decoded.
|
|
|
|
var typeOfNonNullTime = reflect.TypeOf(time.Time{})
|
|
var typeOfNonNullDate = reflect.TypeOf(civil.Date{})
|
|
var typeOfNullString = reflect.TypeOf(NullString{})
|
|
var typeOfNullInt64 = reflect.TypeOf(NullInt64{})
|
|
var typeOfNullBool = reflect.TypeOf(NullBool{})
|
|
var typeOfNullFloat64 = reflect.TypeOf(NullFloat64{})
|
|
var typeOfNullTime = reflect.TypeOf(NullTime{})
|
|
var typeOfNullDate = reflect.TypeOf(NullDate{})
|
|
|
|
// getDecodableSpannerType returns the corresponding decodableSpannerType of
|
|
// the given pointer.
|
|
func getDecodableSpannerType(ptr interface{}) decodableSpannerType {
|
|
kind := reflect.Indirect(reflect.ValueOf(ptr)).Kind()
|
|
if kind == reflect.Invalid {
|
|
return spannerTypeInvalid
|
|
}
|
|
switch kind {
|
|
case reflect.Invalid:
|
|
return spannerTypeInvalid
|
|
case reflect.String:
|
|
return spannerTypeNonNullString
|
|
case reflect.Int64:
|
|
return spannerTypeNonNullInt64
|
|
case reflect.Bool:
|
|
return spannerTypeNonNullBool
|
|
case reflect.Float64:
|
|
return spannerTypeNonNullFloat64
|
|
case reflect.Struct:
|
|
t := reflect.Indirect(reflect.ValueOf(ptr)).Type()
|
|
if t.ConvertibleTo(typeOfNonNullTime) {
|
|
return spannerTypeNonNullTime
|
|
}
|
|
if t.ConvertibleTo(typeOfNonNullDate) {
|
|
return spannerTypeNonNullDate
|
|
}
|
|
if t.ConvertibleTo(typeOfNullString) {
|
|
return spannerTypeNullString
|
|
}
|
|
if t.ConvertibleTo(typeOfNullInt64) {
|
|
return spannerTypeNullInt64
|
|
}
|
|
if t.ConvertibleTo(typeOfNullBool) {
|
|
return spannerTypeNullBool
|
|
}
|
|
if t.ConvertibleTo(typeOfNullFloat64) {
|
|
return spannerTypeNullFloat64
|
|
}
|
|
if t.ConvertibleTo(typeOfNullTime) {
|
|
return spannerTypeNullTime
|
|
}
|
|
if t.ConvertibleTo(typeOfNullDate) {
|
|
return spannerTypeNullDate
|
|
}
|
|
case reflect.Slice:
|
|
kind := reflect.Indirect(reflect.ValueOf(ptr)).Type().Elem().Kind()
|
|
switch kind {
|
|
case reflect.Invalid:
|
|
return spannerTypeUnknown
|
|
case reflect.String:
|
|
return spannerTypeArrayOfNonNullString
|
|
case reflect.Uint8:
|
|
return spannerTypeByteArray
|
|
case reflect.Int64:
|
|
return spannerTypeArrayOfNonNullInt64
|
|
case reflect.Bool:
|
|
return spannerTypeArrayOfNonNullBool
|
|
case reflect.Float64:
|
|
return spannerTypeArrayOfNonNullFloat64
|
|
case reflect.Struct:
|
|
t := reflect.Indirect(reflect.ValueOf(ptr)).Type().Elem()
|
|
if t.ConvertibleTo(typeOfNonNullTime) {
|
|
return spannerTypeArrayOfNonNullTime
|
|
}
|
|
if t.ConvertibleTo(typeOfNonNullDate) {
|
|
return spannerTypeArrayOfNonNullDate
|
|
}
|
|
if t.ConvertibleTo(typeOfNullString) {
|
|
return spannerTypeArrayOfNullString
|
|
}
|
|
if t.ConvertibleTo(typeOfNullInt64) {
|
|
return spannerTypeArrayOfNullInt64
|
|
}
|
|
if t.ConvertibleTo(typeOfNullBool) {
|
|
return spannerTypeArrayOfNullBool
|
|
}
|
|
if t.ConvertibleTo(typeOfNullFloat64) {
|
|
return spannerTypeArrayOfNullFloat64
|
|
}
|
|
if t.ConvertibleTo(typeOfNullTime) {
|
|
return spannerTypeArrayOfNullTime
|
|
}
|
|
if t.ConvertibleTo(typeOfNullDate) {
|
|
return spannerTypeArrayOfNullDate
|
|
}
|
|
case reflect.Slice:
|
|
// The only array-of-array type that is supported is [][]byte.
|
|
kind := reflect.Indirect(reflect.ValueOf(ptr)).Type().Elem().Elem().Kind()
|
|
switch kind {
|
|
case reflect.Uint8:
|
|
return spannerTypeArrayOfByteArray
|
|
}
|
|
}
|
|
}
|
|
// Not convertible to a known base type.
|
|
return spannerTypeUnknown
|
|
}
|
|
|
|
// decodeValueToCustomType decodes a protobuf Value into a pointer to a Go
|
|
// value. It must be possible to convert the value to the type pointed to by
|
|
// the pointer.
|
|
func (dsc decodableSpannerType) decodeValueToCustomType(v *proto3.Value, t *sppb.Type, acode sppb.TypeCode, ptr interface{}) error {
|
|
code := t.Code
|
|
_, isNull := v.Kind.(*proto3.Value_NullValue)
|
|
if dsc == spannerTypeInvalid {
|
|
return errNilDst(ptr)
|
|
}
|
|
if isNull && !dsc.supportsNull() {
|
|
return errDstNotForNull(ptr)
|
|
}
|
|
|
|
var result interface{}
|
|
switch dsc {
|
|
case spannerTypeNonNullString, spannerTypeNullString:
|
|
if code != sppb.TypeCode_STRING {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
result = &NullString{}
|
|
break
|
|
}
|
|
x, err := getStringValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if dsc == spannerTypeNonNullString {
|
|
result = &x
|
|
} else {
|
|
result = &NullString{x, !isNull}
|
|
}
|
|
case spannerTypeByteArray:
|
|
if code != sppb.TypeCode_BYTES {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
result = []byte(nil)
|
|
break
|
|
}
|
|
x, err := getStringValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := base64.StdEncoding.DecodeString(x)
|
|
if err != nil {
|
|
return errBadEncoding(v, err)
|
|
}
|
|
result = y
|
|
case spannerTypeNonNullInt64, spannerTypeNullInt64:
|
|
if code != sppb.TypeCode_INT64 {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
result = &NullInt64{}
|
|
break
|
|
}
|
|
x, err := getStringValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := strconv.ParseInt(x, 10, 64)
|
|
if err != nil {
|
|
return errBadEncoding(v, err)
|
|
}
|
|
if dsc == spannerTypeNonNullInt64 {
|
|
result = &y
|
|
} else {
|
|
result = &NullInt64{y, !isNull}
|
|
}
|
|
case spannerTypeNonNullBool, spannerTypeNullBool:
|
|
if code != sppb.TypeCode_BOOL {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
result = &NullBool{}
|
|
break
|
|
}
|
|
x, err := getBoolValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if dsc == spannerTypeNonNullBool {
|
|
result = &x
|
|
} else {
|
|
result = &NullBool{x, !isNull}
|
|
}
|
|
case spannerTypeNonNullFloat64, spannerTypeNullFloat64:
|
|
if code != sppb.TypeCode_FLOAT64 {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
result = &NullFloat64{}
|
|
break
|
|
}
|
|
x, err := getFloat64Value(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if dsc == spannerTypeNonNullFloat64 {
|
|
result = &x
|
|
} else {
|
|
result = &NullFloat64{x, !isNull}
|
|
}
|
|
case spannerTypeNonNullTime, spannerTypeNullTime:
|
|
var nt NullTime
|
|
err := parseNullTime(v, &nt, code, isNull)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if dsc == spannerTypeNonNullTime {
|
|
result = &nt.Time
|
|
} else {
|
|
result = &nt
|
|
}
|
|
case spannerTypeNonNullDate, spannerTypeNullDate:
|
|
if code != sppb.TypeCode_DATE {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
result = &NullDate{}
|
|
break
|
|
}
|
|
x, err := getStringValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := civil.ParseDate(x)
|
|
if err != nil {
|
|
return errBadEncoding(v, err)
|
|
}
|
|
if dsc == spannerTypeNonNullDate {
|
|
result = &y
|
|
} else {
|
|
result = &NullDate{y, !isNull}
|
|
}
|
|
case spannerTypeArrayOfNonNullString, spannerTypeArrayOfNullString:
|
|
if acode != sppb.TypeCode_STRING {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
ptr = nil
|
|
return nil
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := decodeGenericArray(reflect.TypeOf(ptr).Elem(), x, stringType(), "STRING")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
result = y
|
|
case spannerTypeArrayOfByteArray:
|
|
if acode != sppb.TypeCode_BYTES {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
ptr = nil
|
|
return nil
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := decodeGenericArray(reflect.TypeOf(ptr).Elem(), x, bytesType(), "BYTES")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
result = y
|
|
case spannerTypeArrayOfNonNullInt64, spannerTypeArrayOfNullInt64:
|
|
if acode != sppb.TypeCode_INT64 {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
ptr = nil
|
|
return nil
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := decodeGenericArray(reflect.TypeOf(ptr).Elem(), x, intType(), "INT64")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
result = y
|
|
case spannerTypeArrayOfNonNullBool, spannerTypeArrayOfNullBool:
|
|
if acode != sppb.TypeCode_BOOL {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
ptr = nil
|
|
return nil
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := decodeGenericArray(reflect.TypeOf(ptr).Elem(), x, boolType(), "BOOL")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
result = y
|
|
case spannerTypeArrayOfNonNullFloat64, spannerTypeArrayOfNullFloat64:
|
|
if acode != sppb.TypeCode_FLOAT64 {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
ptr = nil
|
|
return nil
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := decodeGenericArray(reflect.TypeOf(ptr).Elem(), x, floatType(), "FLOAT64")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
result = y
|
|
case spannerTypeArrayOfNonNullTime, spannerTypeArrayOfNullTime:
|
|
if acode != sppb.TypeCode_TIMESTAMP {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
ptr = nil
|
|
return nil
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := decodeGenericArray(reflect.TypeOf(ptr).Elem(), x, timeType(), "TIMESTAMP")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
result = y
|
|
case spannerTypeArrayOfNonNullDate, spannerTypeArrayOfNullDate:
|
|
if acode != sppb.TypeCode_DATE {
|
|
return errTypeMismatch(code, acode, ptr)
|
|
}
|
|
if isNull {
|
|
ptr = nil
|
|
return nil
|
|
}
|
|
x, err := getListValue(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
y, err := decodeGenericArray(reflect.TypeOf(ptr).Elem(), x, dateType(), "DATE")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
result = y
|
|
default:
|
|
// This should not be possible.
|
|
return fmt.Errorf("unknown decodable type found: %v", dsc)
|
|
}
|
|
source := reflect.Indirect(reflect.ValueOf(result))
|
|
destination := reflect.Indirect(reflect.ValueOf(ptr))
|
|
destination.Set(source.Convert(destination.Type()))
|
|
return nil
|
|
}
|
|
|
|
// errSrvVal returns an error for getting a wrong source protobuf value in decoding.
|
|
func errSrcVal(v *proto3.Value, want string) error {
|
|
return spannerErrorf(codes.FailedPrecondition, "cannot use %v(Kind: %T) as %s Value",
|
|
v, v.GetKind(), want)
|
|
}
|
|
|
|
// getStringValue returns the string value encoded in proto3.Value v whose
|
|
// kind is proto3.Value_StringValue.
|
|
func getStringValue(v *proto3.Value) (string, error) {
|
|
if x, ok := v.GetKind().(*proto3.Value_StringValue); ok && x != nil {
|
|
return x.StringValue, nil
|
|
}
|
|
return "", errSrcVal(v, "String")
|
|
}
|
|
|
|
// getBoolValue returns the bool value encoded in proto3.Value v whose
|
|
// kind is proto3.Value_BoolValue.
|
|
func getBoolValue(v *proto3.Value) (bool, error) {
|
|
if x, ok := v.GetKind().(*proto3.Value_BoolValue); ok && x != nil {
|
|
return x.BoolValue, nil
|
|
}
|
|
return false, errSrcVal(v, "Bool")
|
|
}
|
|
|
|
// getListValue returns the proto3.ListValue contained in proto3.Value v whose
|
|
// kind is proto3.Value_ListValue.
|
|
func getListValue(v *proto3.Value) (*proto3.ListValue, error) {
|
|
if x, ok := v.GetKind().(*proto3.Value_ListValue); ok && x != nil {
|
|
return x.ListValue, nil
|
|
}
|
|
return nil, errSrcVal(v, "List")
|
|
}
|
|
|
|
// errUnexpectedNumStr returns error for decoder getting a unexpected string for
|
|
// representing special float values.
|
|
func errUnexpectedNumStr(s string) error {
|
|
return spannerErrorf(codes.FailedPrecondition, "unexpected string value %q for number", s)
|
|
}
|
|
|
|
// getFloat64Value returns the float64 value encoded in proto3.Value v whose
|
|
// kind is proto3.Value_NumberValue / proto3.Value_StringValue.
|
|
// Cloud Spanner uses string to encode NaN, Infinity and -Infinity.
|
|
func getFloat64Value(v *proto3.Value) (float64, error) {
|
|
switch x := v.GetKind().(type) {
|
|
case *proto3.Value_NumberValue:
|
|
if x == nil {
|
|
break
|
|
}
|
|
return x.NumberValue, nil
|
|
case *proto3.Value_StringValue:
|
|
if x == nil {
|
|
break
|
|
}
|
|
switch x.StringValue {
|
|
case "NaN":
|
|
return math.NaN(), nil
|
|
case "Infinity":
|
|
return math.Inf(1), nil
|
|
case "-Infinity":
|
|
return math.Inf(-1), nil
|
|
default:
|
|
return 0, errUnexpectedNumStr(x.StringValue)
|
|
}
|
|
}
|
|
return 0, errSrcVal(v, "Number")
|
|
}
|
|
|
|
// errNilListValue returns error for unexpected nil ListValue in decoding Cloud Spanner ARRAYs.
|
|
func errNilListValue(sqlType string) error {
|
|
return spannerErrorf(codes.FailedPrecondition, "unexpected nil ListValue in decoding %v array", sqlType)
|
|
}
|
|
|
|
// errDecodeArrayElement returns error for failure in decoding single array element.
|
|
func errDecodeArrayElement(i int, v proto.Message, sqlType string, err error) error {
|
|
var se *Error
|
|
if !errorAs(err, &se) {
|
|
return spannerErrorf(codes.Unknown,
|
|
"cannot decode %v(array element %v) as %v, error = <%v>", v, i, sqlType, err)
|
|
}
|
|
se.decorate(fmt.Sprintf("cannot decode %v(array element %v) as %v", v, i, sqlType))
|
|
return se
|
|
}
|
|
|
|
// decodeGenericArray decodes proto3.ListValue pb into a slice which type is
|
|
// determined through reflection.
|
|
func decodeGenericArray(tp reflect.Type, pb *proto3.ListValue, t *sppb.Type, sqlType string) (interface{}, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue(sqlType)
|
|
}
|
|
a := reflect.MakeSlice(tp, len(pb.Values), len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, t, a.Index(i).Addr().Interface()); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "STRING", err)
|
|
}
|
|
}
|
|
return a.Interface(), nil
|
|
}
|
|
|
|
// decodeNullStringArray decodes proto3.ListValue pb into a NullString slice.
|
|
func decodeNullStringArray(pb *proto3.ListValue) ([]NullString, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("STRING")
|
|
}
|
|
a := make([]NullString, len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, stringType(), &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "STRING", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// decodeStringPointerArray decodes proto3.ListValue pb into a *string slice.
|
|
func decodeStringPointerArray(pb *proto3.ListValue) ([]*string, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("STRING")
|
|
}
|
|
a := make([]*string, len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, stringType(), &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "STRING", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// decodeStringArray decodes proto3.ListValue pb into a string slice.
|
|
func decodeStringArray(pb *proto3.ListValue) ([]string, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("STRING")
|
|
}
|
|
a := make([]string, len(pb.Values))
|
|
st := stringType()
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, st, &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "STRING", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// decodeNullInt64Array decodes proto3.ListValue pb into a NullInt64 slice.
|
|
func decodeNullInt64Array(pb *proto3.ListValue) ([]NullInt64, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("INT64")
|
|
}
|
|
a := make([]NullInt64, len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, intType(), &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "INT64", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// decodeInt64PointerArray decodes proto3.ListValue pb into a *int64 slice.
|
|
func decodeInt64PointerArray(pb *proto3.ListValue) ([]*int64, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("INT64")
|
|
}
|
|
a := make([]*int64, len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, intType(), &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "INT64", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// decodeInt64Array decodes proto3.ListValue pb into a int64 slice.
|
|
func decodeInt64Array(pb *proto3.ListValue) ([]int64, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("INT64")
|
|
}
|
|
a := make([]int64, len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, intType(), &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "INT64", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// decodeNullBoolArray decodes proto3.ListValue pb into a NullBool slice.
|
|
func decodeNullBoolArray(pb *proto3.ListValue) ([]NullBool, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("BOOL")
|
|
}
|
|
a := make([]NullBool, len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, boolType(), &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "BOOL", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// decodeBoolPointerArray decodes proto3.ListValue pb into a *bool slice.
|
|
func decodeBoolPointerArray(pb *proto3.ListValue) ([]*bool, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("BOOL")
|
|
}
|
|
a := make([]*bool, len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, boolType(), &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "BOOL", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// decodeBoolArray decodes proto3.ListValue pb into a bool slice.
|
|
func decodeBoolArray(pb *proto3.ListValue) ([]bool, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("BOOL")
|
|
}
|
|
a := make([]bool, len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, boolType(), &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "BOOL", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// decodeNullFloat64Array decodes proto3.ListValue pb into a NullFloat64 slice.
|
|
func decodeNullFloat64Array(pb *proto3.ListValue) ([]NullFloat64, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("FLOAT64")
|
|
}
|
|
a := make([]NullFloat64, len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, floatType(), &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "FLOAT64", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// decodeFloat64PointerArray decodes proto3.ListValue pb into a NullFloat64 slice.
|
|
func decodeFloat64PointerArray(pb *proto3.ListValue) ([]*float64, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("FLOAT64")
|
|
}
|
|
a := make([]*float64, len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, floatType(), &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "FLOAT64", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// decodeFloat64Array decodes proto3.ListValue pb into a float64 slice.
|
|
func decodeFloat64Array(pb *proto3.ListValue) ([]float64, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("FLOAT64")
|
|
}
|
|
a := make([]float64, len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, floatType(), &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "FLOAT64", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// decodeByteArray decodes proto3.ListValue pb into a slice of byte slice.
|
|
func decodeByteArray(pb *proto3.ListValue) ([][]byte, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("BYTES")
|
|
}
|
|
a := make([][]byte, len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, bytesType(), &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "BYTES", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// decodeNullTimeArray decodes proto3.ListValue pb into a NullTime slice.
|
|
func decodeNullTimeArray(pb *proto3.ListValue) ([]NullTime, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("TIMESTAMP")
|
|
}
|
|
a := make([]NullTime, len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, timeType(), &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "TIMESTAMP", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// decodeTimePointerArray decodes proto3.ListValue pb into a NullTime slice.
|
|
func decodeTimePointerArray(pb *proto3.ListValue) ([]*time.Time, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("TIMESTAMP")
|
|
}
|
|
a := make([]*time.Time, len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, timeType(), &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "TIMESTAMP", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// decodeTimeArray decodes proto3.ListValue pb into a time.Time slice.
|
|
func decodeTimeArray(pb *proto3.ListValue) ([]time.Time, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("TIMESTAMP")
|
|
}
|
|
a := make([]time.Time, len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, timeType(), &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "TIMESTAMP", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// decodeNullDateArray decodes proto3.ListValue pb into a NullDate slice.
|
|
func decodeNullDateArray(pb *proto3.ListValue) ([]NullDate, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("DATE")
|
|
}
|
|
a := make([]NullDate, len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, dateType(), &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "DATE", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// decodeDatePointerArray decodes proto3.ListValue pb into a *civil.Date slice.
|
|
func decodeDatePointerArray(pb *proto3.ListValue) ([]*civil.Date, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("DATE")
|
|
}
|
|
a := make([]*civil.Date, len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, dateType(), &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "DATE", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// decodeDateArray decodes proto3.ListValue pb into a civil.Date slice.
|
|
func decodeDateArray(pb *proto3.ListValue) ([]civil.Date, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("DATE")
|
|
}
|
|
a := make([]civil.Date, len(pb.Values))
|
|
for i, v := range pb.Values {
|
|
if err := decodeValue(v, dateType(), &a[i]); err != nil {
|
|
return nil, errDecodeArrayElement(i, v, "DATE", err)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
func errNotStructElement(i int, v *proto3.Value) error {
|
|
return errDecodeArrayElement(i, v, "STRUCT",
|
|
spannerErrorf(codes.FailedPrecondition, "%v(type: %T) doesn't encode Cloud Spanner STRUCT", v, v))
|
|
}
|
|
|
|
// decodeRowArray decodes proto3.ListValue pb into a NullRow slice according to
|
|
// the structural information given in sppb.StructType ty.
|
|
func decodeRowArray(ty *sppb.StructType, pb *proto3.ListValue) ([]NullRow, error) {
|
|
if pb == nil {
|
|
return nil, errNilListValue("STRUCT")
|
|
}
|
|
a := make([]NullRow, len(pb.Values))
|
|
for i := range pb.Values {
|
|
switch v := pb.Values[i].GetKind().(type) {
|
|
case *proto3.Value_ListValue:
|
|
a[i] = NullRow{
|
|
Row: Row{
|
|
fields: ty.Fields,
|
|
vals: v.ListValue.Values,
|
|
},
|
|
Valid: true,
|
|
}
|
|
// Null elements not currently supported by the server, see
|
|
// https://cloud.google.com/spanner/docs/query-syntax#using-structs-with-select
|
|
case *proto3.Value_NullValue:
|
|
// no-op, a[i] is NullRow{} already
|
|
default:
|
|
return nil, errNotStructElement(i, pb.Values[i])
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// errNilSpannerStructType returns error for unexpected nil Cloud Spanner STRUCT
|
|
// schema type in decoding.
|
|
func errNilSpannerStructType() error {
|
|
return spannerErrorf(codes.FailedPrecondition, "unexpected nil StructType in decoding Cloud Spanner STRUCT")
|
|
}
|
|
|
|
// errUnnamedField returns error for decoding a Cloud Spanner STRUCT with
|
|
// unnamed field into a Go struct.
|
|
func errUnnamedField(ty *sppb.StructType, i int) error {
|
|
return spannerErrorf(codes.InvalidArgument, "unnamed field %v in Cloud Spanner STRUCT %+v", i, ty)
|
|
}
|
|
|
|
// errNoOrDupGoField returns error for decoding a Cloud Spanner
|
|
// STRUCT into a Go struct which is either missing a field, or has duplicate
|
|
// fields.
|
|
func errNoOrDupGoField(s interface{}, f string) error {
|
|
return spannerErrorf(codes.InvalidArgument, "Go struct %+v(type %T) has no or duplicate fields for Cloud Spanner STRUCT field %v", s, s, f)
|
|
}
|
|
|
|
// errDupColNames returns error for duplicated Cloud Spanner STRUCT field names
|
|
// found in decoding a Cloud Spanner STRUCT into a Go struct.
|
|
func errDupSpannerField(f string, ty *sppb.StructType) error {
|
|
return spannerErrorf(codes.InvalidArgument, "duplicated field name %q in Cloud Spanner STRUCT %+v", f, ty)
|
|
}
|
|
|
|
// errDecodeStructField returns error for failure in decoding a single field of
|
|
// a Cloud Spanner STRUCT.
|
|
func errDecodeStructField(ty *sppb.StructType, f string, err error) error {
|
|
var se *Error
|
|
if !errorAs(err, &se) {
|
|
return spannerErrorf(codes.Unknown,
|
|
"cannot decode field %v of Cloud Spanner STRUCT %+v, error = <%v>", f, ty, err)
|
|
}
|
|
se.decorate(fmt.Sprintf("cannot decode field %v of Cloud Spanner STRUCT %+v", f, ty))
|
|
return se
|
|
}
|
|
|
|
// decodeStruct decodes proto3.ListValue pb into struct referenced by pointer
|
|
// ptr, according to
|
|
// the structural information given in sppb.StructType ty.
|
|
func decodeStruct(ty *sppb.StructType, pb *proto3.ListValue, ptr interface{}) error {
|
|
if reflect.ValueOf(ptr).IsNil() {
|
|
return errNilDst(ptr)
|
|
}
|
|
if ty == nil {
|
|
return errNilSpannerStructType()
|
|
}
|
|
// t holds the structural information of ptr.
|
|
t := reflect.TypeOf(ptr).Elem()
|
|
// v is the actual value that ptr points to.
|
|
v := reflect.ValueOf(ptr).Elem()
|
|
|
|
fields, err := fieldCache.Fields(t)
|
|
if err != nil {
|
|
return toSpannerError(err)
|
|
}
|
|
seen := map[string]bool{}
|
|
for i, f := range ty.Fields {
|
|
if f.Name == "" {
|
|
return errUnnamedField(ty, i)
|
|
}
|
|
sf := fields.Match(f.Name)
|
|
if sf == nil {
|
|
return errNoOrDupGoField(ptr, f.Name)
|
|
}
|
|
if seen[f.Name] {
|
|
// We don't allow duplicated field name.
|
|
return errDupSpannerField(f.Name, ty)
|
|
}
|
|
// Try to decode a single field.
|
|
if err := decodeValue(pb.Values[i], f.Type, v.FieldByIndex(sf.Index).Addr().Interface()); err != nil {
|
|
return errDecodeStructField(ty, f.Name, err)
|
|
}
|
|
// Mark field f.Name as processed.
|
|
seen[f.Name] = true
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// isPtrStructPtrSlice returns true if ptr is a pointer to a slice of struct pointers.
|
|
func isPtrStructPtrSlice(t reflect.Type) bool {
|
|
if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Slice {
|
|
// t is not a pointer to a slice.
|
|
return false
|
|
}
|
|
if t = t.Elem(); t.Elem().Kind() != reflect.Ptr || t.Elem().Elem().Kind() != reflect.Struct {
|
|
// the slice that t points to is not a slice of struct pointers.
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// decodeStructArray decodes proto3.ListValue pb into struct slice referenced by
|
|
// pointer ptr, according to the
|
|
// structural information given in a sppb.StructType.
|
|
func decodeStructArray(ty *sppb.StructType, pb *proto3.ListValue, ptr interface{}) error {
|
|
if pb == nil {
|
|
return errNilListValue("STRUCT")
|
|
}
|
|
// Type of the struct pointers stored in the slice that ptr points to.
|
|
ts := reflect.TypeOf(ptr).Elem().Elem()
|
|
// The slice that ptr points to, might be nil at this point.
|
|
v := reflect.ValueOf(ptr).Elem()
|
|
// Allocate empty slice.
|
|
v.Set(reflect.MakeSlice(v.Type(), 0, len(pb.Values)))
|
|
// Decode every struct in pb.Values.
|
|
for i, pv := range pb.Values {
|
|
// Check if pv is a NULL value.
|
|
if _, isNull := pv.Kind.(*proto3.Value_NullValue); isNull {
|
|
// Append a nil pointer to the slice.
|
|
v.Set(reflect.Append(v, reflect.New(ts).Elem()))
|
|
continue
|
|
}
|
|
// Allocate empty struct.
|
|
s := reflect.New(ts.Elem())
|
|
// Get proto3.ListValue l from proto3.Value pv.
|
|
l, err := getListValue(pv)
|
|
if err != nil {
|
|
return errDecodeArrayElement(i, pv, "STRUCT", err)
|
|
}
|
|
// Decode proto3.ListValue l into struct referenced by s.Interface().
|
|
if err = decodeStruct(ty, l, s.Interface()); err != nil {
|
|
return errDecodeArrayElement(i, pv, "STRUCT", err)
|
|
}
|
|
// Append the decoded struct back into the slice.
|
|
v.Set(reflect.Append(v, s))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// errEncoderUnsupportedType returns error for not being able to encode a value
|
|
// of certain type.
|
|
func errEncoderUnsupportedType(v interface{}) error {
|
|
return spannerErrorf(codes.InvalidArgument, "client doesn't support type %T", v)
|
|
}
|
|
|
|
// encodeValue encodes a Go native type into a proto3.Value.
|
|
func encodeValue(v interface{}) (*proto3.Value, *sppb.Type, error) {
|
|
pb := &proto3.Value{
|
|
Kind: &proto3.Value_NullValue{NullValue: proto3.NullValue_NULL_VALUE},
|
|
}
|
|
var pt *sppb.Type
|
|
var err error
|
|
switch v := v.(type) {
|
|
case nil:
|
|
case string:
|
|
pb.Kind = stringKind(v)
|
|
pt = stringType()
|
|
case NullString:
|
|
if v.Valid {
|
|
return encodeValue(v.StringVal)
|
|
}
|
|
pt = stringType()
|
|
case []string:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(stringType())
|
|
case []NullString:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(stringType())
|
|
case *string:
|
|
if v != nil {
|
|
return encodeValue(*v)
|
|
}
|
|
pt = stringType()
|
|
case []*string:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(stringType())
|
|
case []byte:
|
|
if v != nil {
|
|
pb.Kind = stringKind(base64.StdEncoding.EncodeToString(v))
|
|
}
|
|
pt = bytesType()
|
|
case [][]byte:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(bytesType())
|
|
case int:
|
|
pb.Kind = stringKind(strconv.FormatInt(int64(v), 10))
|
|
pt = intType()
|
|
case []int:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(intType())
|
|
case int64:
|
|
pb.Kind = stringKind(strconv.FormatInt(v, 10))
|
|
pt = intType()
|
|
case []int64:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(intType())
|
|
case NullInt64:
|
|
if v.Valid {
|
|
return encodeValue(v.Int64)
|
|
}
|
|
pt = intType()
|
|
case []NullInt64:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(intType())
|
|
case *int64:
|
|
if v != nil {
|
|
return encodeValue(*v)
|
|
}
|
|
pt = intType()
|
|
case []*int64:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(intType())
|
|
case bool:
|
|
pb.Kind = &proto3.Value_BoolValue{BoolValue: v}
|
|
pt = boolType()
|
|
case []bool:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(boolType())
|
|
case NullBool:
|
|
if v.Valid {
|
|
return encodeValue(v.Bool)
|
|
}
|
|
pt = boolType()
|
|
case []NullBool:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(boolType())
|
|
case *bool:
|
|
if v != nil {
|
|
return encodeValue(*v)
|
|
}
|
|
pt = boolType()
|
|
case []*bool:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(boolType())
|
|
case float64:
|
|
pb.Kind = &proto3.Value_NumberValue{NumberValue: v}
|
|
pt = floatType()
|
|
case []float64:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(floatType())
|
|
case NullFloat64:
|
|
if v.Valid {
|
|
return encodeValue(v.Float64)
|
|
}
|
|
pt = floatType()
|
|
case []NullFloat64:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(floatType())
|
|
case *float64:
|
|
if v != nil {
|
|
return encodeValue(*v)
|
|
}
|
|
pt = floatType()
|
|
case []*float64:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(floatType())
|
|
case time.Time:
|
|
if v == commitTimestamp {
|
|
pb.Kind = stringKind(commitTimestampPlaceholderString)
|
|
} else {
|
|
pb.Kind = stringKind(v.UTC().Format(time.RFC3339Nano))
|
|
}
|
|
pt = timeType()
|
|
case []time.Time:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(timeType())
|
|
case NullTime:
|
|
if v.Valid {
|
|
return encodeValue(v.Time)
|
|
}
|
|
pt = timeType()
|
|
case []NullTime:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(timeType())
|
|
case *time.Time:
|
|
if v != nil {
|
|
return encodeValue(*v)
|
|
}
|
|
pt = timeType()
|
|
case []*time.Time:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(timeType())
|
|
case civil.Date:
|
|
pb.Kind = stringKind(v.String())
|
|
pt = dateType()
|
|
case []civil.Date:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(dateType())
|
|
case NullDate:
|
|
if v.Valid {
|
|
return encodeValue(v.Date)
|
|
}
|
|
pt = dateType()
|
|
case []NullDate:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(dateType())
|
|
case *civil.Date:
|
|
if v != nil {
|
|
return encodeValue(*v)
|
|
}
|
|
pt = dateType()
|
|
case []*civil.Date:
|
|
if v != nil {
|
|
pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
pt = listType(dateType())
|
|
case GenericColumnValue:
|
|
// Deep clone to ensure subsequent changes to v before
|
|
// transmission don't affect our encoded value.
|
|
pb = proto.Clone(v.Value).(*proto3.Value)
|
|
pt = proto.Clone(v.Type).(*sppb.Type)
|
|
case []GenericColumnValue:
|
|
return nil, nil, errEncoderUnsupportedType(v)
|
|
default:
|
|
// Check if the value is a variant of a base type.
|
|
decodableType := getDecodableSpannerType(v)
|
|
if decodableType != spannerTypeUnknown && decodableType != spannerTypeInvalid {
|
|
converted, err := convertCustomTypeValue(decodableType, v)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return encodeValue(converted)
|
|
}
|
|
|
|
if !isStructOrArrayOfStructValue(v) {
|
|
return nil, nil, errEncoderUnsupportedType(v)
|
|
}
|
|
typ := reflect.TypeOf(v)
|
|
|
|
// Value is a Go struct value/ptr.
|
|
if (typ.Kind() == reflect.Struct) ||
|
|
(typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct) {
|
|
return encodeStruct(v)
|
|
}
|
|
|
|
// Value is a slice of Go struct values/ptrs.
|
|
if typ.Kind() == reflect.Slice {
|
|
return encodeStructArray(v)
|
|
}
|
|
}
|
|
return pb, pt, nil
|
|
}
|
|
|
|
func convertCustomTypeValue(sourceType decodableSpannerType, v interface{}) (interface{}, error) {
|
|
// destination will be initialized to a base type. The input value will be
|
|
// converted to this type and copied to destination.
|
|
var destination reflect.Value
|
|
switch sourceType {
|
|
case spannerTypeInvalid:
|
|
return nil, fmt.Errorf("cannot encode a value to type spannerTypeInvalid")
|
|
case spannerTypeNonNullString:
|
|
destination = reflect.Indirect(reflect.New(reflect.TypeOf("")))
|
|
case spannerTypeNullString:
|
|
destination = reflect.Indirect(reflect.New(reflect.TypeOf(NullString{})))
|
|
case spannerTypeByteArray:
|
|
// Return a nil array directly if the input value is nil instead of
|
|
// creating an empty slice and returning that.
|
|
if reflect.ValueOf(v).IsNil() {
|
|
return []byte(nil), nil
|
|
}
|
|
destination = reflect.MakeSlice(reflect.TypeOf([]byte{}), reflect.ValueOf(v).Len(), reflect.ValueOf(v).Cap())
|
|
case spannerTypeNonNullInt64:
|
|
destination = reflect.Indirect(reflect.New(reflect.TypeOf(int64(0))))
|
|
case spannerTypeNullInt64:
|
|
destination = reflect.Indirect(reflect.New(reflect.TypeOf(NullInt64{})))
|
|
case spannerTypeNonNullBool:
|
|
destination = reflect.Indirect(reflect.New(reflect.TypeOf(false)))
|
|
case spannerTypeNullBool:
|
|
destination = reflect.Indirect(reflect.New(reflect.TypeOf(NullBool{})))
|
|
case spannerTypeNonNullFloat64:
|
|
destination = reflect.Indirect(reflect.New(reflect.TypeOf(float64(0.0))))
|
|
case spannerTypeNullFloat64:
|
|
destination = reflect.Indirect(reflect.New(reflect.TypeOf(NullFloat64{})))
|
|
case spannerTypeNonNullTime:
|
|
destination = reflect.Indirect(reflect.New(reflect.TypeOf(time.Time{})))
|
|
case spannerTypeNullTime:
|
|
destination = reflect.Indirect(reflect.New(reflect.TypeOf(NullTime{})))
|
|
case spannerTypeNonNullDate:
|
|
destination = reflect.Indirect(reflect.New(reflect.TypeOf(civil.Date{})))
|
|
case spannerTypeNullDate:
|
|
destination = reflect.Indirect(reflect.New(reflect.TypeOf(NullDate{})))
|
|
case spannerTypeArrayOfNonNullString:
|
|
if reflect.ValueOf(v).IsNil() {
|
|
return []string(nil), nil
|
|
}
|
|
destination = reflect.MakeSlice(reflect.TypeOf([]string{}), reflect.ValueOf(v).Len(), reflect.ValueOf(v).Cap())
|
|
case spannerTypeArrayOfNullString:
|
|
if reflect.ValueOf(v).IsNil() {
|
|
return []NullString(nil), nil
|
|
}
|
|
destination = reflect.MakeSlice(reflect.TypeOf([]NullString{}), reflect.ValueOf(v).Len(), reflect.ValueOf(v).Cap())
|
|
case spannerTypeArrayOfByteArray:
|
|
if reflect.ValueOf(v).IsNil() {
|
|
return [][]byte(nil), nil
|
|
}
|
|
destination = reflect.MakeSlice(reflect.TypeOf([][]byte{}), reflect.ValueOf(v).Len(), reflect.ValueOf(v).Cap())
|
|
case spannerTypeArrayOfNonNullInt64:
|
|
if reflect.ValueOf(v).IsNil() {
|
|
return []int64(nil), nil
|
|
}
|
|
destination = reflect.MakeSlice(reflect.TypeOf([]int64{}), reflect.ValueOf(v).Len(), reflect.ValueOf(v).Cap())
|
|
case spannerTypeArrayOfNullInt64:
|
|
if reflect.ValueOf(v).IsNil() {
|
|
return []NullInt64(nil), nil
|
|
}
|
|
destination = reflect.MakeSlice(reflect.TypeOf([]NullInt64{}), reflect.ValueOf(v).Len(), reflect.ValueOf(v).Cap())
|
|
case spannerTypeArrayOfNonNullBool:
|
|
if reflect.ValueOf(v).IsNil() {
|
|
return []bool(nil), nil
|
|
}
|
|
destination = reflect.MakeSlice(reflect.TypeOf([]bool{}), reflect.ValueOf(v).Len(), reflect.ValueOf(v).Cap())
|
|
case spannerTypeArrayOfNullBool:
|
|
if reflect.ValueOf(v).IsNil() {
|
|
return []NullBool(nil), nil
|
|
}
|
|
destination = reflect.MakeSlice(reflect.TypeOf([]NullBool{}), reflect.ValueOf(v).Len(), reflect.ValueOf(v).Cap())
|
|
case spannerTypeArrayOfNonNullFloat64:
|
|
if reflect.ValueOf(v).IsNil() {
|
|
return []float64(nil), nil
|
|
}
|
|
destination = reflect.MakeSlice(reflect.TypeOf([]float64{}), reflect.ValueOf(v).Len(), reflect.ValueOf(v).Cap())
|
|
case spannerTypeArrayOfNullFloat64:
|
|
if reflect.ValueOf(v).IsNil() {
|
|
return []NullFloat64(nil), nil
|
|
}
|
|
destination = reflect.MakeSlice(reflect.TypeOf([]NullFloat64{}), reflect.ValueOf(v).Len(), reflect.ValueOf(v).Cap())
|
|
case spannerTypeArrayOfNonNullTime:
|
|
if reflect.ValueOf(v).IsNil() {
|
|
return []time.Time(nil), nil
|
|
}
|
|
destination = reflect.MakeSlice(reflect.TypeOf([]time.Time{}), reflect.ValueOf(v).Len(), reflect.ValueOf(v).Cap())
|
|
case spannerTypeArrayOfNullTime:
|
|
if reflect.ValueOf(v).IsNil() {
|
|
return []NullTime(nil), nil
|
|
}
|
|
destination = reflect.MakeSlice(reflect.TypeOf([]NullTime{}), reflect.ValueOf(v).Len(), reflect.ValueOf(v).Cap())
|
|
case spannerTypeArrayOfNonNullDate:
|
|
if reflect.ValueOf(v).IsNil() {
|
|
return []civil.Date(nil), nil
|
|
}
|
|
destination = reflect.MakeSlice(reflect.TypeOf([]civil.Date{}), reflect.ValueOf(v).Len(), reflect.ValueOf(v).Cap())
|
|
case spannerTypeArrayOfNullDate:
|
|
if reflect.ValueOf(v).IsNil() {
|
|
return []NullDate(nil), nil
|
|
}
|
|
destination = reflect.MakeSlice(reflect.TypeOf([]NullDate{}), reflect.ValueOf(v).Len(), reflect.ValueOf(v).Cap())
|
|
default:
|
|
// This should not be possible.
|
|
return nil, fmt.Errorf("unknown decodable type found: %v", sourceType)
|
|
}
|
|
// destination has been initialized. Convert and copy the input value to
|
|
// destination. That must be done per element if the input type is a slice
|
|
// or an array.
|
|
if destination.Kind() == reflect.Slice || destination.Kind() == reflect.Array {
|
|
sourceSlice := reflect.ValueOf(v)
|
|
for i := 0; i < destination.Len(); i++ {
|
|
source := reflect.Indirect(sourceSlice.Index(i))
|
|
destination.Index(i).Set(source.Convert(destination.Type().Elem()))
|
|
}
|
|
} else {
|
|
source := reflect.Indirect(reflect.ValueOf(v))
|
|
destination.Set(source.Convert(destination.Type()))
|
|
}
|
|
// Return the converted value.
|
|
return destination.Interface(), nil
|
|
}
|
|
|
|
// Encodes a Go struct value/ptr in v to the spanner Value and Type protos. v
|
|
// itself must be non-nil.
|
|
func encodeStruct(v interface{}) (*proto3.Value, *sppb.Type, error) {
|
|
typ := reflect.TypeOf(v)
|
|
val := reflect.ValueOf(v)
|
|
|
|
// Pointer to struct.
|
|
if typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct {
|
|
typ = typ.Elem()
|
|
if val.IsNil() {
|
|
// nil pointer to struct, representing a NULL STRUCT value. Use a
|
|
// dummy value to get the type.
|
|
_, st, err := encodeStruct(reflect.Zero(typ).Interface())
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return nullProto(), st, nil
|
|
}
|
|
val = val.Elem()
|
|
}
|
|
|
|
if typ.Kind() != reflect.Struct {
|
|
return nil, nil, errEncoderUnsupportedType(v)
|
|
}
|
|
|
|
stf := make([]*sppb.StructType_Field, 0, typ.NumField())
|
|
stv := make([]*proto3.Value, 0, typ.NumField())
|
|
|
|
for i := 0; i < typ.NumField(); i++ {
|
|
// If the field has a 'spanner' tag, use the value of that tag as the field name.
|
|
// This is used to build STRUCT types with unnamed/duplicate fields.
|
|
sf := typ.Field(i)
|
|
fval := val.Field(i)
|
|
|
|
// Embedded fields are not allowed.
|
|
if sf.Anonymous {
|
|
return nil, nil, errUnsupportedEmbeddedStructFields(sf.Name)
|
|
}
|
|
|
|
// Unexported fields are ignored.
|
|
if !fval.CanInterface() {
|
|
continue
|
|
}
|
|
|
|
fname, ok := sf.Tag.Lookup("spanner")
|
|
if !ok {
|
|
fname = sf.Name
|
|
}
|
|
|
|
eval, etype, err := encodeValue(fval.Interface())
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
stf = append(stf, mkField(fname, etype))
|
|
stv = append(stv, eval)
|
|
}
|
|
|
|
return listProto(stv...), structType(stf...), nil
|
|
}
|
|
|
|
// Encodes a slice of Go struct values/ptrs in v to the spanner Value and Type
|
|
// protos. v itself must be non-nil.
|
|
func encodeStructArray(v interface{}) (*proto3.Value, *sppb.Type, error) {
|
|
etyp := reflect.TypeOf(v).Elem()
|
|
sliceval := reflect.ValueOf(v)
|
|
|
|
// Slice of pointers to structs.
|
|
if etyp.Kind() == reflect.Ptr {
|
|
etyp = etyp.Elem()
|
|
}
|
|
|
|
// Use a dummy struct value to get the element type.
|
|
_, elemTyp, err := encodeStruct(reflect.Zero(etyp).Interface())
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// nil slice represents a NULL array-of-struct.
|
|
if sliceval.IsNil() {
|
|
return nullProto(), listType(elemTyp), nil
|
|
}
|
|
|
|
values := make([]*proto3.Value, 0, sliceval.Len())
|
|
|
|
for i := 0; i < sliceval.Len(); i++ {
|
|
ev, _, err := encodeStruct(sliceval.Index(i).Interface())
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
values = append(values, ev)
|
|
}
|
|
return listProto(values...), listType(elemTyp), nil
|
|
}
|
|
|
|
func isStructOrArrayOfStructValue(v interface{}) bool {
|
|
typ := reflect.TypeOf(v)
|
|
if typ.Kind() == reflect.Slice {
|
|
typ = typ.Elem()
|
|
}
|
|
if typ.Kind() == reflect.Ptr {
|
|
typ = typ.Elem()
|
|
}
|
|
return typ.Kind() == reflect.Struct
|
|
}
|
|
|
|
func isSupportedMutationType(v interface{}) bool {
|
|
switch v.(type) {
|
|
case nil, string, *string, NullString, []string, []*string, []NullString,
|
|
[]byte, [][]byte,
|
|
int, []int, int64, *int64, []int64, []*int64, NullInt64, []NullInt64,
|
|
bool, *bool, []bool, []*bool, NullBool, []NullBool,
|
|
float64, *float64, []float64, []*float64, NullFloat64, []NullFloat64,
|
|
time.Time, *time.Time, []time.Time, []*time.Time, NullTime, []NullTime,
|
|
civil.Date, *civil.Date, []civil.Date, []*civil.Date, NullDate, []NullDate,
|
|
GenericColumnValue:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// encodeValueArray encodes a Value array into a proto3.ListValue.
|
|
func encodeValueArray(vs []interface{}) (*proto3.ListValue, error) {
|
|
lv := &proto3.ListValue{}
|
|
lv.Values = make([]*proto3.Value, 0, len(vs))
|
|
for _, v := range vs {
|
|
if !isSupportedMutationType(v) {
|
|
return nil, errEncoderUnsupportedType(v)
|
|
}
|
|
pb, _, err := encodeValue(v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
lv.Values = append(lv.Values, pb)
|
|
}
|
|
return lv, nil
|
|
}
|
|
|
|
// encodeArray assumes that all values of the array element type encode without
|
|
// error.
|
|
func encodeArray(len int, at func(int) interface{}) (*proto3.Value, error) {
|
|
vs := make([]*proto3.Value, len)
|
|
var err error
|
|
for i := 0; i < len; i++ {
|
|
vs[i], _, err = encodeValue(at(i))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return listProto(vs...), nil
|
|
}
|
|
|
|
func spannerTagParser(t reflect.StructTag) (name string, keep bool, other interface{}, err error) {
|
|
if s := t.Get("spanner"); s != "" {
|
|
if s == "-" {
|
|
return "", false, nil, nil
|
|
}
|
|
return s, true, nil, nil
|
|
}
|
|
return "", true, nil, nil
|
|
}
|
|
|
|
var fieldCache = fields.NewCache(spannerTagParser, nil, nil)
|
|
|
|
func trimDoubleQuotes(payload []byte) ([]byte, error) {
|
|
if len(payload) <= 1 || payload[0] != '"' || payload[len(payload)-1] != '"' {
|
|
return nil, fmt.Errorf("payload is too short or not wrapped with double quotes: got %q", string(payload))
|
|
}
|
|
// Remove the double quotes at the beginning and the end.
|
|
return payload[1 : len(payload)-1], nil
|
|
}
|