Switch to using the dep tool and update all the dependencies
This commit is contained in:
+307
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
Copyright 2017 Google Inc. All Rights Reserved.
|
||||
|
||||
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 (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
proto3 "github.com/golang/protobuf/ptypes/struct"
|
||||
|
||||
sppb "google.golang.org/genproto/googleapis/spanner/v1"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
// A Row is a view of a row of data produced by a Cloud Spanner read.
|
||||
//
|
||||
// A row consists of a number of columns; the number depends on the columns
|
||||
// used to construct the read.
|
||||
//
|
||||
// The column values can be accessed by index, where the indices are with
|
||||
// respect to the columns. For instance, if the read specified
|
||||
// []string{"photo_id", "caption", "metadata"}, then each row will
|
||||
// contain three columns: the 0th column corresponds to "photo_id", the
|
||||
// 1st column corresponds to "caption", etc.
|
||||
//
|
||||
// Column values are decoded by using one of the Column, ColumnByName, or
|
||||
// Columns methods. The valid values passed to these methods depend on the
|
||||
// column type. For example:
|
||||
//
|
||||
// var photoID int64
|
||||
// err := row.Column(0, &photoID) // Decode column 0 as an integer.
|
||||
//
|
||||
// var caption string
|
||||
// err := row.Column(1, &caption) // Decode column 1 as a string.
|
||||
//
|
||||
// // The above two operations at once.
|
||||
// err := row.Columns(&photoID, &caption)
|
||||
//
|
||||
// Supported types and their corresponding Cloud Spanner column type(s) are:
|
||||
//
|
||||
// *string(not NULL), *NullString - STRING
|
||||
// *[]NullString - STRING ARRAY
|
||||
// *[]byte - BYTES
|
||||
// *[][]byte - BYTES ARRAY
|
||||
// *int64(not NULL), *NullInt64 - INT64
|
||||
// *[]NullInt64 - INT64 ARRAY
|
||||
// *bool(not NULL), *NullBool - BOOL
|
||||
// *[]NullBool - BOOL ARRAY
|
||||
// *float64(not NULL), *NullFloat64 - FLOAT64
|
||||
// *[]NullFloat64 - FLOAT64 ARRAY
|
||||
// *time.Time(not NULL), *NullTime - TIMESTAMP
|
||||
// *[]NullTime - TIMESTAMP ARRAY
|
||||
// *Date(not NULL), *NullDate - DATE
|
||||
// *[]NullDate - DATE ARRAY
|
||||
// *[]*some_go_struct, *[]NullRow - STRUCT ARRAY
|
||||
// *GenericColumnValue - any Cloud Spanner type
|
||||
//
|
||||
// For TIMESTAMP columns, returned time.Time object will be in UTC.
|
||||
//
|
||||
// To fetch an array of BYTES, pass a *[][]byte. To fetch an array of
|
||||
// (sub)rows, pass a *[]spanner.NullRow or a *[]*some_go_struct where
|
||||
// some_go_struct holds all information of the subrow, see spannr.Row.ToStruct
|
||||
// for the mapping between Cloud Spanner row and Go struct. To fetch an array of
|
||||
// other types, pass a *[]spanner.Null* type of the appropriate type. Use
|
||||
// *GenericColumnValue when you don't know in advance what column type to
|
||||
// expect.
|
||||
//
|
||||
// Row decodes the row contents lazily; as a result, each call to a getter has
|
||||
// a chance of returning an error.
|
||||
//
|
||||
// A column value may be NULL if the corresponding value is not present in
|
||||
// Cloud Spanner. The spanner.Null* types (spanner.NullInt64 et al.) allow fetching
|
||||
// values that may be null. A NULL BYTES can be fetched into a *[]byte as nil.
|
||||
// It is an error to fetch a NULL value into any other type.
|
||||
type Row struct {
|
||||
fields []*sppb.StructType_Field
|
||||
vals []*proto3.Value // keep decoded for now
|
||||
}
|
||||
|
||||
// errNamesValuesMismatch returns error for when columnNames count is not equal
|
||||
// to columnValues count.
|
||||
func errNamesValuesMismatch(columnNames []string, columnValues []interface{}) error {
|
||||
return spannerErrorf(codes.FailedPrecondition,
|
||||
"different number of names(%v) and values(%v)", len(columnNames), len(columnValues))
|
||||
}
|
||||
|
||||
// NewRow returns a Row containing the supplied data. This can be useful for
|
||||
// mocking Cloud Spanner Read and Query responses for unit testing.
|
||||
func NewRow(columnNames []string, columnValues []interface{}) (*Row, error) {
|
||||
if len(columnValues) != len(columnNames) {
|
||||
return nil, errNamesValuesMismatch(columnNames, columnValues)
|
||||
}
|
||||
r := Row{
|
||||
fields: make([]*sppb.StructType_Field, len(columnValues)),
|
||||
vals: make([]*proto3.Value, len(columnValues)),
|
||||
}
|
||||
for i := range columnValues {
|
||||
val, typ, err := encodeValue(columnValues[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.fields[i] = &sppb.StructType_Field{
|
||||
Name: columnNames[i],
|
||||
Type: typ,
|
||||
}
|
||||
r.vals[i] = val
|
||||
}
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
// Size is the number of columns in the row.
|
||||
func (r *Row) Size() int {
|
||||
return len(r.fields)
|
||||
}
|
||||
|
||||
// ColumnName returns the name of column i, or empty string for invalid column.
|
||||
func (r *Row) ColumnName(i int) string {
|
||||
if i < 0 || i >= len(r.fields) {
|
||||
return ""
|
||||
}
|
||||
return r.fields[i].Name
|
||||
}
|
||||
|
||||
// ColumnIndex returns the index of the column with the given name. The
|
||||
// comparison is case-sensitive.
|
||||
func (r *Row) ColumnIndex(name string) (int, error) {
|
||||
found := false
|
||||
var index int
|
||||
if len(r.vals) != len(r.fields) {
|
||||
return 0, errFieldsMismatchVals(r)
|
||||
}
|
||||
for i, f := range r.fields {
|
||||
if f == nil {
|
||||
return 0, errNilColType(i)
|
||||
}
|
||||
if name == f.Name {
|
||||
if found {
|
||||
return 0, errDupColName(name)
|
||||
}
|
||||
found = true
|
||||
index = i
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return 0, errColNotFound(name)
|
||||
}
|
||||
return index, nil
|
||||
}
|
||||
|
||||
// ColumnNames returns all column names of the row.
|
||||
func (r *Row) ColumnNames() []string {
|
||||
var n []string
|
||||
for _, c := range r.fields {
|
||||
n = append(n, c.Name)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// errColIdxOutOfRange returns error for requested column index is out of the
|
||||
// range of the target Row's columns.
|
||||
func errColIdxOutOfRange(i int, r *Row) error {
|
||||
return spannerErrorf(codes.OutOfRange, "column index %d out of range [0,%d)", i, len(r.vals))
|
||||
}
|
||||
|
||||
// errDecodeColumn returns error for not being able to decode a indexed column.
|
||||
func errDecodeColumn(i int, err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
se, ok := toSpannerError(err).(*Error)
|
||||
if !ok {
|
||||
return spannerErrorf(codes.InvalidArgument, "failed to decode column %v, error = <%v>", i, err)
|
||||
}
|
||||
se.decorate(fmt.Sprintf("failed to decode column %v", i))
|
||||
return se
|
||||
}
|
||||
|
||||
// errFieldsMismatchVals returns error for field count isn't equal to value count in a Row.
|
||||
func errFieldsMismatchVals(r *Row) error {
|
||||
return spannerErrorf(codes.FailedPrecondition, "row has different number of fields(%v) and values(%v)",
|
||||
len(r.fields), len(r.vals))
|
||||
}
|
||||
|
||||
// errNilColType returns error for column type for column i being nil in the row.
|
||||
func errNilColType(i int) error {
|
||||
return spannerErrorf(codes.FailedPrecondition, "column(%v)'s type is nil", i)
|
||||
}
|
||||
|
||||
// Column fetches the value from the ith column, decoding it into ptr.
|
||||
// See the Row documentation for the list of acceptable argument types.
|
||||
func (r *Row) Column(i int, ptr interface{}) error {
|
||||
if len(r.vals) != len(r.fields) {
|
||||
return errFieldsMismatchVals(r)
|
||||
}
|
||||
if i < 0 || i >= len(r.fields) {
|
||||
return errColIdxOutOfRange(i, r)
|
||||
}
|
||||
if r.fields[i] == nil {
|
||||
return errNilColType(i)
|
||||
}
|
||||
if err := decodeValue(r.vals[i], r.fields[i].Type, ptr); err != nil {
|
||||
return errDecodeColumn(i, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// errDupColName returns error for duplicated column name in the same row.
|
||||
func errDupColName(n string) error {
|
||||
return spannerErrorf(codes.FailedPrecondition, "ambiguous column name %q", n)
|
||||
}
|
||||
|
||||
// errColNotFound returns error for not being able to find a named column.
|
||||
func errColNotFound(n string) error {
|
||||
return spannerErrorf(codes.NotFound, "column %q not found", n)
|
||||
}
|
||||
|
||||
// ColumnByName fetches the value from the named column, decoding it into ptr.
|
||||
// See the Row documentation for the list of acceptable argument types.
|
||||
func (r *Row) ColumnByName(name string, ptr interface{}) error {
|
||||
index, err := r.ColumnIndex(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.Column(index, ptr)
|
||||
}
|
||||
|
||||
// errNumOfColValue returns error for providing wrong number of values to Columns.
|
||||
func errNumOfColValue(n int, r *Row) error {
|
||||
return spannerErrorf(codes.InvalidArgument,
|
||||
"Columns(): number of arguments (%d) does not match row size (%d)", n, len(r.vals))
|
||||
}
|
||||
|
||||
// Columns fetches all the columns in the row at once.
|
||||
//
|
||||
// The value of the kth column will be decoded into the kth argument to
|
||||
// Columns. See above for the list of acceptable argument types. The number of
|
||||
// arguments must be equal to the number of columns. Pass nil to specify that a
|
||||
// column should be ignored.
|
||||
func (r *Row) Columns(ptrs ...interface{}) error {
|
||||
if len(ptrs) != len(r.vals) {
|
||||
return errNumOfColValue(len(ptrs), r)
|
||||
}
|
||||
if len(r.vals) != len(r.fields) {
|
||||
return errFieldsMismatchVals(r)
|
||||
}
|
||||
for i, p := range ptrs {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
if err := r.Column(i, p); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// errToStructArgType returns error for p not having the correct data type(pointer to Go struct) to
|
||||
// be the argument of Row.ToStruct.
|
||||
func errToStructArgType(p interface{}) error {
|
||||
return spannerErrorf(codes.InvalidArgument, "ToStruct(): type %T is not a valid pointer to Go struct", p)
|
||||
}
|
||||
|
||||
// ToStruct fetches the columns in a row into the fields of a struct.
|
||||
// The rules for mapping a row's columns into a struct's exported fields
|
||||
// are as the following:
|
||||
// 1. If a field has a `spanner: "column_name"` tag, then decode column
|
||||
// 'column_name' into the field. A special case is the `spanner: "-"`
|
||||
// tag, which instructs ToStruct to ignore the field during decoding.
|
||||
// 2. Otherwise, if the name of a field matches the name of a column (ignoring case),
|
||||
// decode the column into the field.
|
||||
//
|
||||
// The fields of the destination struct can be of any type that is acceptable
|
||||
// to (*spanner.Row).Column.
|
||||
//
|
||||
// Slice and pointer fields will be set to nil if the source column
|
||||
// is NULL, and a non-nil value if the column is not NULL. To decode NULL
|
||||
// values of other types, use one of the spanner.Null* as the type of the
|
||||
// destination field.
|
||||
func (r *Row) ToStruct(p interface{}) error {
|
||||
// Check if p is a pointer to a struct
|
||||
if t := reflect.TypeOf(p); t == nil || t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct {
|
||||
return errToStructArgType(p)
|
||||
}
|
||||
if len(r.vals) != len(r.fields) {
|
||||
return errFieldsMismatchVals(r)
|
||||
}
|
||||
// Call decodeStruct directly to decode the row as a typed proto.ListValue.
|
||||
return decodeStruct(
|
||||
&sppb.StructType{Fields: r.fields},
|
||||
&proto3.ListValue{Values: r.vals},
|
||||
p,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user