| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- // Copyright 2011 Google Inc. All rights reserved.
- // Use of this source code is governed by the Apache 2.0
- // license that can be found in the LICENSE file.
- package datastore
- import (
- "fmt"
- "reflect"
- "strings"
- "time"
- "github.com/golang/protobuf/proto"
- "google.golang.org/appengine"
- pb "google.golang.org/appengine/internal/datastore"
- )
- var (
- typeOfBlobKey = reflect.TypeOf(appengine.BlobKey(""))
- typeOfByteSlice = reflect.TypeOf([]byte(nil))
- typeOfByteString = reflect.TypeOf(ByteString(nil))
- typeOfGeoPoint = reflect.TypeOf(appengine.GeoPoint{})
- typeOfTime = reflect.TypeOf(time.Time{})
- typeOfKeyPtr = reflect.TypeOf(&Key{})
- typeOfEntityPtr = reflect.TypeOf(&Entity{})
- )
- // typeMismatchReason returns a string explaining why the property p could not
- // be stored in an entity field of type v.Type().
- func typeMismatchReason(pValue interface{}, v reflect.Value) string {
- entityType := "empty"
- switch pValue.(type) {
- case int64:
- entityType = "int"
- case bool:
- entityType = "bool"
- case string:
- entityType = "string"
- case float64:
- entityType = "float"
- case *Key:
- entityType = "*datastore.Key"
- case time.Time:
- entityType = "time.Time"
- case appengine.BlobKey:
- entityType = "appengine.BlobKey"
- case appengine.GeoPoint:
- entityType = "appengine.GeoPoint"
- case ByteString:
- entityType = "datastore.ByteString"
- case []byte:
- entityType = "[]byte"
- }
- return fmt.Sprintf("type mismatch: %s versus %v", entityType, v.Type())
- }
- type propertyLoader struct {
- // m holds the number of times a substruct field like "Foo.Bar.Baz" has
- // been seen so far. The map is constructed lazily.
- m map[string]int
- }
- func (l *propertyLoader) load(codec *structCodec, structValue reflect.Value, p Property, requireSlice bool) string {
- var v reflect.Value
- var sliceIndex int
- name := p.Name
- // If name ends with a '.', the last field is anonymous.
- // In this case, strings.Split will give us "" as the
- // last element of our fields slice, which will match the ""
- // field name in the substruct codec.
- fields := strings.Split(name, ".")
- for len(fields) > 0 {
- var decoder fieldCodec
- var ok bool
- // Cut off the last field (delimited by ".") and find its parent
- // in the codec.
- // eg. for name "A.B.C.D", split off "A.B.C" and try to
- // find a field in the codec with this name.
- // Loop again with "A.B", etc.
- for i := len(fields); i > 0; i-- {
- parent := strings.Join(fields[:i], ".")
- decoder, ok = codec.fields[parent]
- if ok {
- fields = fields[i:]
- break
- }
- }
- // If we never found a matching field in the codec, return
- // error message.
- if !ok {
- return "no such struct field"
- }
- v = initField(structValue, decoder.path)
- if !v.IsValid() {
- return "no such struct field"
- }
- if !v.CanSet() {
- return "cannot set struct field"
- }
- if decoder.structCodec != nil {
- codec = decoder.structCodec
- structValue = v
- }
- if v.Kind() == reflect.Slice && v.Type() != typeOfByteSlice {
- if l.m == nil {
- l.m = make(map[string]int)
- }
- sliceIndex = l.m[p.Name]
- l.m[p.Name] = sliceIndex + 1
- for v.Len() <= sliceIndex {
- v.Set(reflect.Append(v, reflect.New(v.Type().Elem()).Elem()))
- }
- structValue = v.Index(sliceIndex)
- requireSlice = false
- }
- }
- var slice reflect.Value
- if v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8 {
- slice = v
- v = reflect.New(v.Type().Elem()).Elem()
- } else if requireSlice {
- return "multiple-valued property requires a slice field type"
- }
- // Convert indexValues to a Go value with a meaning derived from the
- // destination type.
- pValue := p.Value
- if iv, ok := pValue.(indexValue); ok {
- meaning := pb.Property_NO_MEANING
- switch v.Type() {
- case typeOfBlobKey:
- meaning = pb.Property_BLOBKEY
- case typeOfByteSlice:
- meaning = pb.Property_BLOB
- case typeOfByteString:
- meaning = pb.Property_BYTESTRING
- case typeOfGeoPoint:
- meaning = pb.Property_GEORSS_POINT
- case typeOfTime:
- meaning = pb.Property_GD_WHEN
- case typeOfEntityPtr:
- meaning = pb.Property_ENTITY_PROTO
- }
- var err error
- pValue, err = propValue(iv.value, meaning)
- if err != nil {
- return err.Error()
- }
- }
- if errReason := setVal(v, pValue); errReason != "" {
- // Set the slice back to its zero value.
- if slice.IsValid() {
- slice.Set(reflect.Zero(slice.Type()))
- }
- return errReason
- }
- if slice.IsValid() {
- slice.Index(sliceIndex).Set(v)
- }
- return ""
- }
- // setVal sets v to the value pValue.
- func setVal(v reflect.Value, pValue interface{}) string {
- switch v.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- x, ok := pValue.(int64)
- if !ok && pValue != nil {
- return typeMismatchReason(pValue, v)
- }
- if v.OverflowInt(x) {
- return fmt.Sprintf("value %v overflows struct field of type %v", x, v.Type())
- }
- v.SetInt(x)
- case reflect.Bool:
- x, ok := pValue.(bool)
- if !ok && pValue != nil {
- return typeMismatchReason(pValue, v)
- }
- v.SetBool(x)
- case reflect.String:
- switch x := pValue.(type) {
- case appengine.BlobKey:
- v.SetString(string(x))
- case ByteString:
- v.SetString(string(x))
- case string:
- v.SetString(x)
- default:
- if pValue != nil {
- return typeMismatchReason(pValue, v)
- }
- }
- case reflect.Float32, reflect.Float64:
- x, ok := pValue.(float64)
- if !ok && pValue != nil {
- return typeMismatchReason(pValue, v)
- }
- if v.OverflowFloat(x) {
- return fmt.Sprintf("value %v overflows struct field of type %v", x, v.Type())
- }
- v.SetFloat(x)
- case reflect.Ptr:
- x, ok := pValue.(*Key)
- if !ok && pValue != nil {
- return typeMismatchReason(pValue, v)
- }
- if _, ok := v.Interface().(*Key); !ok {
- return typeMismatchReason(pValue, v)
- }
- v.Set(reflect.ValueOf(x))
- case reflect.Struct:
- switch v.Type() {
- case typeOfTime:
- x, ok := pValue.(time.Time)
- if !ok && pValue != nil {
- return typeMismatchReason(pValue, v)
- }
- v.Set(reflect.ValueOf(x))
- case typeOfGeoPoint:
- x, ok := pValue.(appengine.GeoPoint)
- if !ok && pValue != nil {
- return typeMismatchReason(pValue, v)
- }
- v.Set(reflect.ValueOf(x))
- default:
- ent, ok := pValue.(*Entity)
- if !ok {
- return typeMismatchReason(pValue, v)
- }
- // Recursively load nested struct
- pls, err := newStructPLS(v.Addr().Interface())
- if err != nil {
- return err.Error()
- }
- // if ent has a Key value and our struct has a Key field,
- // load the Entity's Key value into the Key field on the struct.
- if ent.Key != nil && pls.codec.keyField != -1 {
- pls.v.Field(pls.codec.keyField).Set(reflect.ValueOf(ent.Key))
- }
- err = pls.Load(ent.Properties)
- if err != nil {
- return err.Error()
- }
- }
- case reflect.Slice:
- x, ok := pValue.([]byte)
- if !ok {
- if y, yok := pValue.(ByteString); yok {
- x, ok = []byte(y), true
- }
- }
- if !ok && pValue != nil {
- return typeMismatchReason(pValue, v)
- }
- if v.Type().Elem().Kind() != reflect.Uint8 {
- return typeMismatchReason(pValue, v)
- }
- v.SetBytes(x)
- default:
- return typeMismatchReason(pValue, v)
- }
- return ""
- }
- // initField is similar to reflect's Value.FieldByIndex, in that it
- // returns the nested struct field corresponding to index, but it
- // initialises any nil pointers encountered when traversing the structure.
- func initField(val reflect.Value, index []int) reflect.Value {
- for _, i := range index[:len(index)-1] {
- val = val.Field(i)
- if val.Kind() == reflect.Ptr {
- if val.IsNil() {
- val.Set(reflect.New(val.Type().Elem()))
- }
- val = val.Elem()
- }
- }
- return val.Field(index[len(index)-1])
- }
- // loadEntity loads an EntityProto into PropertyLoadSaver or struct pointer.
- func loadEntity(dst interface{}, src *pb.EntityProto) (err error) {
- ent, err := protoToEntity(src)
- if err != nil {
- return err
- }
- if e, ok := dst.(PropertyLoadSaver); ok {
- return e.Load(ent.Properties)
- }
- return LoadStruct(dst, ent.Properties)
- }
- func (s structPLS) Load(props []Property) error {
- var fieldName, reason string
- var l propertyLoader
- for _, p := range props {
- if errStr := l.load(s.codec, s.v, p, p.Multiple); errStr != "" {
- // We don't return early, as we try to load as many properties as possible.
- // It is valid to load an entity into a struct that cannot fully represent it.
- // That case returns an error, but the caller is free to ignore it.
- fieldName, reason = p.Name, errStr
- }
- }
- if reason != "" {
- return &ErrFieldMismatch{
- StructType: s.v.Type(),
- FieldName: fieldName,
- Reason: reason,
- }
- }
- return nil
- }
- func protoToEntity(src *pb.EntityProto) (*Entity, error) {
- props, rawProps := src.Property, src.RawProperty
- outProps := make([]Property, 0, len(props)+len(rawProps))
- for {
- var (
- x *pb.Property
- noIndex bool
- )
- if len(props) > 0 {
- x, props = props[0], props[1:]
- } else if len(rawProps) > 0 {
- x, rawProps = rawProps[0], rawProps[1:]
- noIndex = true
- } else {
- break
- }
- var value interface{}
- if x.Meaning != nil && *x.Meaning == pb.Property_INDEX_VALUE {
- value = indexValue{x.Value}
- } else {
- var err error
- value, err = propValue(x.Value, x.GetMeaning())
- if err != nil {
- return nil, err
- }
- }
- outProps = append(outProps, Property{
- Name: x.GetName(),
- Value: value,
- NoIndex: noIndex,
- Multiple: x.GetMultiple(),
- })
- }
- var key *Key
- if src.Key != nil {
- // Ignore any error, since nested entity values
- // are allowed to have an invalid key.
- key, _ = protoToKey(src.Key)
- }
- return &Entity{key, outProps}, nil
- }
- // propValue returns a Go value that combines the raw PropertyValue with a
- // meaning. For example, an Int64Value with GD_WHEN becomes a time.Time.
- func propValue(v *pb.PropertyValue, m pb.Property_Meaning) (interface{}, error) {
- switch {
- case v.Int64Value != nil:
- if m == pb.Property_GD_WHEN {
- return fromUnixMicro(*v.Int64Value), nil
- } else {
- return *v.Int64Value, nil
- }
- case v.BooleanValue != nil:
- return *v.BooleanValue, nil
- case v.StringValue != nil:
- if m == pb.Property_BLOB {
- return []byte(*v.StringValue), nil
- } else if m == pb.Property_BLOBKEY {
- return appengine.BlobKey(*v.StringValue), nil
- } else if m == pb.Property_BYTESTRING {
- return ByteString(*v.StringValue), nil
- } else if m == pb.Property_ENTITY_PROTO {
- var ent pb.EntityProto
- err := proto.Unmarshal([]byte(*v.StringValue), &ent)
- if err != nil {
- return nil, err
- }
- return protoToEntity(&ent)
- } else {
- return *v.StringValue, nil
- }
- case v.DoubleValue != nil:
- return *v.DoubleValue, nil
- case v.Referencevalue != nil:
- key, err := referenceValueToKey(v.Referencevalue)
- if err != nil {
- return nil, err
- }
- return key, nil
- case v.Pointvalue != nil:
- // NOTE: Strangely, latitude maps to X, longitude to Y.
- return appengine.GeoPoint{Lat: v.Pointvalue.GetX(), Lng: v.Pointvalue.GetY()}, nil
- }
- return nil, nil
- }
- // indexValue is a Property value that is created when entities are loaded from
- // an index, such as from a projection query.
- //
- // Such Property values do not contain all of the metadata required to be
- // faithfully represented as a Go value, and are instead represented as an
- // opaque indexValue. Load the properties into a concrete struct type (e.g. by
- // passing a struct pointer to Iterator.Next) to reconstruct actual Go values
- // of type int, string, time.Time, etc.
- type indexValue struct {
- value *pb.PropertyValue
- }
|