tessl install tessl/golang-cloud-google-com--go--spanner@1.87.2Official Google Cloud Spanner client library for Go providing comprehensive database operations, transactions, and admin functionality
Comprehensive guide to Spanner data types, null handling, encoding/decoding, and type conversions.
// Scalar types
string, *string, NullString → STRING
[]byte → BYTES
int, int64, *int64, NullInt64 → INT64
bool, *bool, NullBool → BOOL
float64, *float64, NullFloat64 → FLOAT64
float32, *float32, NullFloat32 → FLOAT32
time.Time, *time.Time, NullTime → TIMESTAMP
civil.Date, *civil.Date, NullDate → DATE
big.Rat, *big.Rat, NullNumeric → NUMERIC
uuid.UUID, *uuid.UUID, NullUUID → UUID (GoogleSQL only)
NullJSON → JSON
PGJsonB → JSONB (PostgreSQL only)
PGNumeric → NUMERIC (PostgreSQL only)
protoreflect.Enum, NullProtoEnum → ENUM
proto.Message, NullProtoMessage → PROTO
Interval, NullInterval → INTERVAL
// Array types
[]string, []*string, []NullString → ARRAY<STRING>
[][]byte → ARRAY<BYTES>
[]int, []int64, []*int64, []NullInt64 → ARRAY<INT64>
[]bool, []*bool, []NullBool → ARRAY<BOOL>
[]float64, []*float64, []NullFloat64 → ARRAY<FLOAT64>
[]float32, []*float32, []NullFloat32 → ARRAY<FLOAT32>
[]time.Time, []*time.Time, []NullTime → ARRAY<TIMESTAMP>
[]civil.Date, []*civil.Date, []NullDate → ARRAY<DATE>
[]big.Rat, []*big.Rat, []NullNumeric → ARRAY<NUMERIC>
[]uuid.UUID, []*uuid.UUID, []NullUUID → ARRAY<UUID>
[]NullJSON → ARRAY<JSON>All nullable Spanner types use NullXXX wrappers:
type NullString struct {
StringVal string
Valid bool
}
func (n *NullString) IsNull() bool
func (n *NullString) MarshalJSON() ([]byte, error)
func (n *NullString) UnmarshalJSON(payload []byte) error
func (n *NullString) Scan(value interface{}) error
func (n *NullString) Value() (driver.Value, error)
func (n *NullString) String() string
func (n *NullString) GormDataType() stringtype NullInt64 struct {
Int64 int64
Valid bool
}
func (n *NullInt64) IsNull() bool
func (n *NullInt64) MarshalJSON() ([]byte, error)
func (n *NullInt64) UnmarshalJSON(payload []byte) error
func (n *NullInt64) Scan(value interface{}) error
func (n NullInt64) Value() (driver.Value, error)
func (n NullInt64) String() string
func (n NullInt64) GormDataType() stringtype NullFloat64 struct {
Float64 float64
Valid bool
}
func (n *NullFloat64) IsNull() bool
func (n *NullFloat64) MarshalJSON() ([]byte, error)
func (n *NullFloat64) UnmarshalJSON(payload []byte) error
func (n *NullFloat64) Scan(value interface{}) error
func (n NullFloat64) Value() (driver.Value, error)
func (n NullFloat64) String() string
func (n NullFloat64) GormDataType() stringtype NullFloat32 struct {
Float32 float32
Valid bool
}
func (n *NullFloat32) IsNull() bool
func (n *NullFloat32) MarshalJSON() ([]byte, error)
func (n *NullFloat32) UnmarshalJSON(payload []byte) error
func (n *NullFloat32) Scan(value interface{}) error
func (n NullFloat32) Value() (driver.Value, error)
func (n NullFloat32) String() string
func (n NullFloat32) GormDataType() stringtype NullBool struct {
Bool bool
Valid bool
}
func (n *NullBool) IsNull() bool
func (n *NullBool) MarshalJSON() ([]byte, error)
func (n *NullBool) UnmarshalJSON(payload []byte) error
func (n *NullBool) Scan(value interface{}) error
func (n NullBool) Value() (driver.Value, error)
func (n NullBool) String() string
func (n NullBool) GormDataType() stringtype NullTime struct {
Time time.Time
Valid bool
}type NullDate struct {
Date civil.Date
Valid bool
}type NullNumeric struct {
Numeric big.Rat
Valid bool
}
func NumericString(r *big.Rat) stringConstants:
const (
NumericPrecisionDigits = 38 // Max digits
NumericScaleDigits = 9 // Max decimal digits
)
var LossOfPrecisionHandling LossOfPrecisionHandlingOption
type LossOfPrecisionHandlingOption int
const (
NumericRound LossOfPrecisionHandlingOption = iota
NumericError
)type NullJSON struct {
Value interface{}
Valid bool
}
func UseNumberWithJSONDecoderEncoder(useNumber bool)type NullUUID struct {
UUID uuid.UUID
Valid bool
}type Interval struct {
Months int32
Days int32
Nanos *big.Int
}
func ParseInterval(s string) (Interval, error)
func (i *Interval) String() string
type NullInterval struct {
Interval Interval
Valid bool
}type NullProtoEnum struct {
ProtoEnumVal protoreflect.Enum
Valid bool
}
type NullProtoMessage struct {
ProtoMessageVal proto.Message
Valid bool
}PostgreSQL-specific types:
type PGJsonB struct {
Value interface{}
Valid bool
}
type PGNumeric struct {
Numeric string
Valid bool
}All null value wrapper types implement this interface:
type NullableValue interface {
IsNull() bool
}type Row struct {
// Has unexported fields
}
func NewRow(columnNames []string, columnValues []interface{}) (*Row, error)func (r *Row) Column(i int, ptr interface{}) error
func (r *Row) ColumnByName(name string, ptr interface{}) error
func (r *Row) Columns(ptrs ...interface{}) error
func (r *Row) ToStruct(p interface{}) error
func (r *Row) ToStructLenient(p interface{}) error
func (r *Row) Size() int
func (r *Row) ColumnNames() []string
func (r *Row) ColumnName(i int) string
func (r *Row) ColumnIndex(name string) (int, error)
func (r *Row) ColumnType(i int) *sppb.Type
func (r *Row) ColumnValue(i int) *proto3.Value
func (r *Row) String() stringDecoding Examples:
// By position
var name string
var age int64
err := row.Columns(&name, &age)
// By name
var email string
err := row.ColumnByName("email", &email)
// To struct
type User struct {
Name string `spanner:"name"`
Email string `spanner:"email"`
Age int64 `spanner:"age"`
}
var user User
err := row.ToStruct(&user)
// Lenient decode (ignore extra columns)
err := row.ToStructLenient(&user)type Encoder interface {
EncodeSpanner() (interface{}, error)
}Example:
type CustomField struct {
Prefix string
Suffix string
}
func (cf CustomField) EncodeSpanner() (interface{}, error) {
return fmt.Sprintf("%s-%s", cf.Prefix, cf.Suffix), nil
}type Decoder interface {
DecodeSpanner(input interface{}) error
}Example:
func (cf *CustomField) DecodeSpanner(val interface{}) error {
strVal, ok := val.(string)
if !ok {
return fmt.Errorf("expected string, got %T", val)
}
parts := strings.Split(strVal, "-")
if len(parts) != 2 {
return fmt.Errorf("invalid format")
}
cf.Prefix = parts[0]
cf.Suffix = parts[1]
return nil
}For dynamic typing:
type GenericColumnValue struct {
Type *sppb.Type
Value *proto3.Value
}
func (g *GenericColumnValue) Decode(ptr interface{}) errorExample:
var gcv spanner.GenericColumnValue
err := row.Column(0, &gcv)
// Decode to specific type
var str string
err = gcv.Decode(&str)For STRUCT types:
type NullRow struct {
Row Row
Valid bool
}type DecodeOptions interface {
Apply(s *decodeSetting)
}
func WithLenient() DecodeOptionsfunc SelectAll(rows rowIterator, destination interface{}, options ...DecodeOptions) errorExample:
var users []User
stmt := spanner.NewStatement("SELECT * FROM Users")
iter := client.Single().Query(ctx, stmt)
err := spanner.SelectAll(iter, &users)type User struct {
ID int64 `spanner:"user_id"` // Map to user_id column
Name string `spanner:"full_name"` // Map to full_name column
Email string // Maps to "email" (lowercase field name)
Internal string `spanner:"-"` // Ignore field
}