| // Copyright 2014 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 datastore |
| |
| import ( |
| "fmt" |
| "reflect" |
| "strings" |
| "time" |
| |
| "cloud.google.com/go/civil" |
| "cloud.google.com/go/internal/fields" |
| pb "google.golang.org/genproto/googleapis/datastore/v1" |
| ) |
| |
| var ( |
| typeOfByteSlice = reflect.TypeOf([]byte(nil)) |
| typeOfTime = reflect.TypeOf(time.Time{}) |
| typeOfCivilDate = reflect.TypeOf(civil.Date{}) |
| typeOfCivilDateTime = reflect.TypeOf(civil.DateTime{}) |
| typeOfCivilTime = reflect.TypeOf(civil.Time{}) |
| typeOfGeoPoint = reflect.TypeOf(GeoPoint{}) |
| typeOfKeyPtr = reflect.TypeOf(&Key{}) |
| ) |
| |
| // typeMismatchReason returns a string explaining why the property p could not |
| // be stored in an entity field of type v.Type(). |
| func typeMismatchReason(p Property, v reflect.Value) string { |
| entityType := "empty" |
| switch p.Value.(type) { |
| case int64: |
| entityType = "int" |
| case bool: |
| entityType = "bool" |
| case string: |
| entityType = "string" |
| case float64: |
| entityType = "float" |
| case *Key: |
| entityType = "*datastore.Key" |
| case *Entity: |
| entityType = "*datastore.Entity" |
| case GeoPoint: |
| entityType = "GeoPoint" |
| case time.Time: |
| entityType = "time.Time" |
| case []byte: |
| entityType = "[]byte" |
| } |
| |
| return fmt.Sprintf("type mismatch: %s versus %v", entityType, v.Type()) |
| } |
| |
| func overflowReason(x interface{}, v reflect.Value) string { |
| return fmt.Sprintf("value %v overflows struct field of type %v", x, 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 fields.List, structValue reflect.Value, p Property, prev map[string]struct{}) string { |
| sl, ok := p.Value.([]interface{}) |
| if !ok { |
| return l.loadOneElement(codec, structValue, p, prev) |
| } |
| for _, val := range sl { |
| p.Value = val |
| if errStr := l.loadOneElement(codec, structValue, p, prev); errStr != "" { |
| return errStr |
| } |
| } |
| return "" |
| } |
| |
| // loadOneElement loads the value of Property p into structValue based on the provided |
| // codec. codec is used to find the field in structValue into which p should be loaded. |
| // prev is the set of property names already seen for structValue. |
| func (l *propertyLoader) loadOneElement(codec fields.List, structValue reflect.Value, p Property, prev map[string]struct{}) string { |
| var sliceOk bool |
| var sliceIndex int |
| var v reflect.Value |
| |
| name := p.Name |
| fieldNames := strings.Split(name, ".") |
| |
| for len(fieldNames) > 0 { |
| var field *fields.Field |
| |
| // Start by trying to find a field with name. If none found, |
| // 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(fieldNames); i > 0; i-- { |
| parent := strings.Join(fieldNames[:i], ".") |
| field = codec.Match(parent) |
| if field != nil { |
| fieldNames = fieldNames[i:] |
| break |
| } |
| } |
| |
| // If we never found a matching field in the codec, return |
| // error message. |
| if field == nil { |
| return "no such struct field" |
| } |
| |
| v = initField(structValue, field.Index) |
| if !v.IsValid() { |
| return "no such struct field" |
| } |
| if !v.CanSet() { |
| return "cannot set struct field" |
| } |
| |
| // If field implements PLS, we delegate loading to the PLS's Load early, |
| // and stop iterating through fields. |
| ok, err := plsFieldLoad(v, p, fieldNames) |
| if err != nil { |
| return err.Error() |
| } |
| if ok { |
| return "" |
| } |
| |
| if field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct { |
| codec, err = structCache.Fields(field.Type.Elem()) |
| if err != nil { |
| return err.Error() |
| } |
| |
| // Init value if its nil |
| if v.IsNil() { |
| v.Set(reflect.New(field.Type.Elem())) |
| } |
| structValue = v.Elem() |
| } |
| |
| if field.Type.Kind() == reflect.Struct { |
| codec, err = structCache.Fields(field.Type) |
| if err != nil { |
| return err.Error() |
| } |
| structValue = v |
| } |
| |
| // If the element is a slice, we need to accommodate it. |
| 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) |
| |
| // If structValue implements PLS, we delegate loading to the PLS's |
| // Load early, and stop iterating through fields. |
| ok, err := plsFieldLoad(structValue, p, fieldNames) |
| if err != nil { |
| return err.Error() |
| } |
| if ok { |
| return "" |
| } |
| |
| if structValue.Type().Kind() == reflect.Struct { |
| codec, err = structCache.Fields(structValue.Type()) |
| if err != nil { |
| return err.Error() |
| } |
| } |
| sliceOk = true |
| } |
| } |
| |
| 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 _, ok := prev[p.Name]; ok && !sliceOk { |
| // Zero the field back out that was set previously, turns out |
| // it's a slice and we don't know what to do with it |
| v.Set(reflect.Zero(v.Type())) |
| return "multiple-valued property requires a slice field type" |
| } |
| |
| prev[p.Name] = struct{}{} |
| |
| if errReason := setVal(v, p); 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 "" |
| } |
| |
| // plsFieldLoad first tries to converts v's value to a PLS, then v's addressed |
| // value to a PLS. If neither succeeds, plsFieldLoad returns false for first return |
| // value. Otherwise, the first return value will be true. |
| // If v is successfully converted to a PLS, plsFieldLoad will then try to Load |
| // the property p into v (by way of the PLS's Load method). |
| // |
| // If the field v has been flattened, the Property's name must be altered |
| // before calling Load to reflect the field v. |
| // For example, if our original field name was "A.B.C.D", |
| // and at this point in iteration we had initialized the field |
| // corresponding to "A" and have moved into the struct, so that now |
| // v corresponds to the field named "B", then we want to let the |
| // PLS handle this field (B)'s subfields ("C", "D"), |
| // so we send the property to the PLS's Load, renamed to "C.D". |
| // |
| // If subfields are present, the field v has been flattened. |
| func plsFieldLoad(v reflect.Value, p Property, subfields []string) (ok bool, err error) { |
| vpls, err := plsForLoad(v) |
| if err != nil { |
| return false, err |
| } |
| |
| if vpls == nil { |
| return false, nil |
| } |
| |
| // If Entity, load properties as well as key. |
| if e, ok := p.Value.(*Entity); ok { |
| err = loadEntity(vpls, e) |
| return true, err |
| } |
| |
| // If flattened, we must alter the property's name to reflect |
| // the field v. |
| if len(subfields) > 0 { |
| p.Name = strings.Join(subfields, ".") |
| } |
| |
| return true, vpls.Load([]Property{p}) |
| } |
| |
| // setVal sets 'v' to the value of the Property 'p'. |
| func setVal(v reflect.Value, p Property) (s string) { |
| pValue := p.Value |
| switch v.Kind() { |
| case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
| x, ok := pValue.(int64) |
| if !ok && pValue != nil { |
| return typeMismatchReason(p, v) |
| } |
| if v.OverflowInt(x) { |
| return overflowReason(x, v) |
| } |
| v.SetInt(x) |
| case reflect.Bool: |
| x, ok := pValue.(bool) |
| if !ok && pValue != nil { |
| return typeMismatchReason(p, v) |
| } |
| v.SetBool(x) |
| case reflect.String: |
| x, ok := pValue.(string) |
| if !ok && pValue != nil { |
| return typeMismatchReason(p, v) |
| } |
| v.SetString(x) |
| case reflect.Float32, reflect.Float64: |
| x, ok := pValue.(float64) |
| if !ok && pValue != nil { |
| return typeMismatchReason(p, v) |
| } |
| if v.OverflowFloat(x) { |
| return overflowReason(x, v) |
| } |
| v.SetFloat(x) |
| |
| case reflect.Interface: |
| if !v.CanSet() { |
| return fmt.Sprintf("%v is unsettable", v.Type()) |
| } |
| |
| rpValue := reflect.ValueOf(pValue) |
| if !rpValue.Type().AssignableTo(v.Type()) { |
| return fmt.Sprintf("%q is not assignable to %q", rpValue.Type(), v.Type()) |
| } |
| v.Set(rpValue) |
| |
| case reflect.Ptr: |
| // v must be a pointer to either a Key, an Entity, or one of the supported basic types. |
| if v.Type() != typeOfKeyPtr && v.Type().Elem().Kind() != reflect.Struct && !isValidPointerType(v.Type().Elem()) { |
| return typeMismatchReason(p, v) |
| } |
| |
| if pValue == nil { |
| // If v is populated already, set it to nil. |
| if !v.IsNil() { |
| v.Set(reflect.New(v.Type()).Elem()) |
| } |
| return "" |
| } |
| |
| if x, ok := p.Value.(*Key); ok { |
| if _, ok := v.Interface().(*Key); !ok { |
| return typeMismatchReason(p, v) |
| } |
| v.Set(reflect.ValueOf(x)) |
| return "" |
| } |
| if v.IsNil() { |
| v.Set(reflect.New(v.Type().Elem())) |
| } |
| switch x := pValue.(type) { |
| case *Entity: |
| err := loadEntity(v.Interface(), x) |
| if err != nil { |
| return err.Error() |
| } |
| case int64: |
| if v.Elem().OverflowInt(x) { |
| return overflowReason(x, v.Elem()) |
| } |
| v.Elem().SetInt(x) |
| case float64: |
| if v.Elem().OverflowFloat(x) { |
| return overflowReason(x, v.Elem()) |
| } |
| v.Elem().SetFloat(x) |
| case bool: |
| v.Elem().SetBool(x) |
| case string: |
| v.Elem().SetString(x) |
| case GeoPoint, time.Time: |
| v.Elem().Set(reflect.ValueOf(x)) |
| default: |
| return typeMismatchReason(p, v) |
| } |
| case reflect.Struct: |
| switch v.Type() { |
| case typeOfTime: |
| // Some time values are converted into microsecond integer values |
| // (for example when used with projects). So, here we check first |
| // whether this value is an int64, and next whether it's time. |
| // |
| // See more at https://cloud.google.com/datastore/docs/concepts/queries#limitations_on_projections |
| micros, ok := pValue.(int64) |
| if ok { |
| s := micros / 1e6 |
| ns := micros % 1e6 |
| v.Set(reflect.ValueOf(time.Unix(s, ns).In(time.UTC))) |
| break |
| } |
| x, ok := pValue.(time.Time) |
| if !ok && pValue != nil { |
| return typeMismatchReason(p, v) |
| } |
| v.Set(reflect.ValueOf(x)) |
| case typeOfGeoPoint: |
| x, ok := pValue.(GeoPoint) |
| if !ok && pValue != nil { |
| return typeMismatchReason(p, v) |
| } |
| v.Set(reflect.ValueOf(x)) |
| case typeOfCivilDate: |
| date := civil.DateOf(pValue.(time.Time).In(time.UTC)) |
| v.Set(reflect.ValueOf(date)) |
| case typeOfCivilDateTime: |
| dateTime := civil.DateTimeOf(pValue.(time.Time).In(time.UTC)) |
| v.Set(reflect.ValueOf(dateTime)) |
| case typeOfCivilTime: |
| timeVal := civil.TimeOf(pValue.(time.Time).In(time.UTC)) |
| v.Set(reflect.ValueOf(timeVal)) |
| default: |
| ent, ok := pValue.(*Entity) |
| if !ok { |
| return typeMismatchReason(p, v) |
| } |
| err := loadEntity(v.Addr().Interface(), ent) |
| if err != nil { |
| return err.Error() |
| } |
| } |
| case reflect.Slice: |
| x, ok := pValue.([]byte) |
| if !ok && pValue != nil { |
| return typeMismatchReason(p, v) |
| } |
| if v.Type().Elem().Kind() != reflect.Uint8 { |
| return typeMismatchReason(p, v) |
| } |
| v.SetBytes(x) |
| default: |
| return typeMismatchReason(p, 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]) |
| } |
| |
| // loadEntityProto loads an EntityProto into PropertyLoadSaver or struct pointer. |
| func loadEntityProto(dst interface{}, src *pb.Entity) error { |
| ent, err := protoToEntity(src) |
| if err != nil { |
| return err |
| } |
| return loadEntity(dst, ent) |
| } |
| |
| func loadEntity(dst interface{}, ent *Entity) error { |
| if pls, ok := dst.(PropertyLoadSaver); ok { |
| // Load both key and properties. Try to load as much as possible, even |
| // if an error occurs during loading either the key or the |
| // properties. |
| var keyLoadErr error |
| if e, ok := dst.(KeyLoader); ok { |
| keyLoadErr = e.LoadKey(ent.Key) |
| } |
| loadErr := pls.Load(ent.Properties) |
| // Let any error returned by LoadKey prevail above any error from Load. |
| if keyLoadErr != nil { |
| return keyLoadErr |
| } |
| return loadErr |
| } |
| return loadEntityToStruct(dst, ent) |
| } |
| |
| func loadEntityToStruct(dst interface{}, ent *Entity) error { |
| pls, err := newStructPLS(dst) |
| if err != nil { |
| return err |
| } |
| |
| // Try and load key. |
| keyField := pls.codec.Match(keyFieldName) |
| if keyField != nil && ent.Key != nil { |
| pls.v.FieldByIndex(keyField.Index).Set(reflect.ValueOf(ent.Key)) |
| } |
| |
| // Load properties. |
| return pls.Load(ent.Properties) |
| } |
| |
| func (s structPLS) Load(props []Property) error { |
| var fieldName, errReason string |
| var l propertyLoader |
| |
| prev := make(map[string]struct{}) |
| for _, p := range props { |
| if errStr := l.load(s.codec, s.v, p, prev); 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, errReason = p.Name, errStr |
| } |
| } |
| if errReason != "" { |
| return &ErrFieldMismatch{ |
| StructType: s.v.Type(), |
| FieldName: fieldName, |
| Reason: errReason, |
| } |
| } |
| return nil |
| } |
| |
| func protoToEntity(src *pb.Entity) (*Entity, error) { |
| props := make([]Property, 0, len(src.Properties)) |
| for name, val := range src.Properties { |
| v, err := propToValue(val) |
| if err != nil { |
| return nil, err |
| } |
| props = append(props, Property{ |
| Name: name, |
| Value: v, |
| NoIndex: val.ExcludeFromIndexes, |
| }) |
| } |
| 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, props}, nil |
| } |
| |
| // propToValue returns a Go value that represents the PropertyValue. For |
| // example, a TimestampValue becomes a time.Time. |
| func propToValue(v *pb.Value) (interface{}, error) { |
| switch v := v.ValueType.(type) { |
| case *pb.Value_NullValue: |
| return nil, nil |
| case *pb.Value_BooleanValue: |
| return v.BooleanValue, nil |
| case *pb.Value_IntegerValue: |
| return v.IntegerValue, nil |
| case *pb.Value_DoubleValue: |
| return v.DoubleValue, nil |
| case *pb.Value_TimestampValue: |
| return time.Unix(v.TimestampValue.Seconds, int64(v.TimestampValue.Nanos)).In(time.UTC), nil |
| case *pb.Value_KeyValue: |
| return protoToKey(v.KeyValue) |
| case *pb.Value_StringValue: |
| return v.StringValue, nil |
| case *pb.Value_BlobValue: |
| return []byte(v.BlobValue), nil |
| case *pb.Value_GeoPointValue: |
| return GeoPoint{Lat: v.GeoPointValue.Latitude, Lng: v.GeoPointValue.Longitude}, nil |
| case *pb.Value_EntityValue: |
| return protoToEntity(v.EntityValue) |
| case *pb.Value_ArrayValue: |
| arr := make([]interface{}, 0, len(v.ArrayValue.Values)) |
| for _, v := range v.ArrayValue.Values { |
| vv, err := propToValue(v) |
| if err != nil { |
| return nil, err |
| } |
| arr = append(arr, vv) |
| } |
| return arr, nil |
| default: |
| return nil, nil |
| } |
| } |