Home | History | Annotate | Download | only in datastore
      1 // Copyright 2011 Google Inc. All rights reserved.
      2 // Use of this source code is governed by the Apache 2.0
      3 // license that can be found in the LICENSE file.
      4 
      5 package datastore
      6 
      7 import (
      8 	"errors"
      9 	"fmt"
     10 	"reflect"
     11 
     12 	"github.com/golang/protobuf/proto"
     13 	"golang.org/x/net/context"
     14 
     15 	"google.golang.org/appengine"
     16 	"google.golang.org/appengine/internal"
     17 	pb "google.golang.org/appengine/internal/datastore"
     18 )
     19 
     20 var (
     21 	// ErrInvalidEntityType is returned when functions like Get or Next are
     22 	// passed a dst or src argument of invalid type.
     23 	ErrInvalidEntityType = errors.New("datastore: invalid entity type")
     24 	// ErrInvalidKey is returned when an invalid key is presented.
     25 	ErrInvalidKey = errors.New("datastore: invalid key")
     26 	// ErrNoSuchEntity is returned when no entity was found for a given key.
     27 	ErrNoSuchEntity = errors.New("datastore: no such entity")
     28 )
     29 
     30 // ErrFieldMismatch is returned when a field is to be loaded into a different
     31 // type than the one it was stored from, or when a field is missing or
     32 // unexported in the destination struct.
     33 // StructType is the type of the struct pointed to by the destination argument
     34 // passed to Get or to Iterator.Next.
     35 type ErrFieldMismatch struct {
     36 	StructType reflect.Type
     37 	FieldName  string
     38 	Reason     string
     39 }
     40 
     41 func (e *ErrFieldMismatch) Error() string {
     42 	return fmt.Sprintf("datastore: cannot load field %q into a %q: %s",
     43 		e.FieldName, e.StructType, e.Reason)
     44 }
     45 
     46 // protoToKey converts a Reference proto to a *Key. If the key is invalid,
     47 // protoToKey will return the invalid key along with ErrInvalidKey.
     48 func protoToKey(r *pb.Reference) (k *Key, err error) {
     49 	appID := r.GetApp()
     50 	namespace := r.GetNameSpace()
     51 	for _, e := range r.Path.Element {
     52 		k = &Key{
     53 			kind:      e.GetType(),
     54 			stringID:  e.GetName(),
     55 			intID:     e.GetId(),
     56 			parent:    k,
     57 			appID:     appID,
     58 			namespace: namespace,
     59 		}
     60 		if !k.valid() {
     61 			return k, ErrInvalidKey
     62 		}
     63 	}
     64 	return
     65 }
     66 
     67 // keyToProto converts a *Key to a Reference proto.
     68 func keyToProto(defaultAppID string, k *Key) *pb.Reference {
     69 	appID := k.appID
     70 	if appID == "" {
     71 		appID = defaultAppID
     72 	}
     73 	n := 0
     74 	for i := k; i != nil; i = i.parent {
     75 		n++
     76 	}
     77 	e := make([]*pb.Path_Element, n)
     78 	for i := k; i != nil; i = i.parent {
     79 		n--
     80 		e[n] = &pb.Path_Element{
     81 			Type: &i.kind,
     82 		}
     83 		// At most one of {Name,Id} should be set.
     84 		// Neither will be set for incomplete keys.
     85 		if i.stringID != "" {
     86 			e[n].Name = &i.stringID
     87 		} else if i.intID != 0 {
     88 			e[n].Id = &i.intID
     89 		}
     90 	}
     91 	var namespace *string
     92 	if k.namespace != "" {
     93 		namespace = proto.String(k.namespace)
     94 	}
     95 	return &pb.Reference{
     96 		App:       proto.String(appID),
     97 		NameSpace: namespace,
     98 		Path: &pb.Path{
     99 			Element: e,
    100 		},
    101 	}
    102 }
    103 
    104 // multiKeyToProto is a batch version of keyToProto.
    105 func multiKeyToProto(appID string, key []*Key) []*pb.Reference {
    106 	ret := make([]*pb.Reference, len(key))
    107 	for i, k := range key {
    108 		ret[i] = keyToProto(appID, k)
    109 	}
    110 	return ret
    111 }
    112 
    113 // multiValid is a batch version of Key.valid. It returns an error, not a
    114 // []bool.
    115 func multiValid(key []*Key) error {
    116 	invalid := false
    117 	for _, k := range key {
    118 		if !k.valid() {
    119 			invalid = true
    120 			break
    121 		}
    122 	}
    123 	if !invalid {
    124 		return nil
    125 	}
    126 	err := make(appengine.MultiError, len(key))
    127 	for i, k := range key {
    128 		if !k.valid() {
    129 			err[i] = ErrInvalidKey
    130 		}
    131 	}
    132 	return err
    133 }
    134 
    135 // It's unfortunate that the two semantically equivalent concepts pb.Reference
    136 // and pb.PropertyValue_ReferenceValue aren't the same type. For example, the
    137 // two have different protobuf field numbers.
    138 
    139 // referenceValueToKey is the same as protoToKey except the input is a
    140 // PropertyValue_ReferenceValue instead of a Reference.
    141 func referenceValueToKey(r *pb.PropertyValue_ReferenceValue) (k *Key, err error) {
    142 	appID := r.GetApp()
    143 	namespace := r.GetNameSpace()
    144 	for _, e := range r.Pathelement {
    145 		k = &Key{
    146 			kind:      e.GetType(),
    147 			stringID:  e.GetName(),
    148 			intID:     e.GetId(),
    149 			parent:    k,
    150 			appID:     appID,
    151 			namespace: namespace,
    152 		}
    153 		if !k.valid() {
    154 			return nil, ErrInvalidKey
    155 		}
    156 	}
    157 	return
    158 }
    159 
    160 // keyToReferenceValue is the same as keyToProto except the output is a
    161 // PropertyValue_ReferenceValue instead of a Reference.
    162 func keyToReferenceValue(defaultAppID string, k *Key) *pb.PropertyValue_ReferenceValue {
    163 	ref := keyToProto(defaultAppID, k)
    164 	pe := make([]*pb.PropertyValue_ReferenceValue_PathElement, len(ref.Path.Element))
    165 	for i, e := range ref.Path.Element {
    166 		pe[i] = &pb.PropertyValue_ReferenceValue_PathElement{
    167 			Type: e.Type,
    168 			Id:   e.Id,
    169 			Name: e.Name,
    170 		}
    171 	}
    172 	return &pb.PropertyValue_ReferenceValue{
    173 		App:         ref.App,
    174 		NameSpace:   ref.NameSpace,
    175 		Pathelement: pe,
    176 	}
    177 }
    178 
    179 type multiArgType int
    180 
    181 const (
    182 	multiArgTypeInvalid multiArgType = iota
    183 	multiArgTypePropertyLoadSaver
    184 	multiArgTypeStruct
    185 	multiArgTypeStructPtr
    186 	multiArgTypeInterface
    187 )
    188 
    189 // checkMultiArg checks that v has type []S, []*S, []I, or []P, for some struct
    190 // type S, for some interface type I, or some non-interface non-pointer type P
    191 // such that P or *P implements PropertyLoadSaver.
    192 //
    193 // It returns what category the slice's elements are, and the reflect.Type
    194 // that represents S, I or P.
    195 //
    196 // As a special case, PropertyList is an invalid type for v.
    197 func checkMultiArg(v reflect.Value) (m multiArgType, elemType reflect.Type) {
    198 	if v.Kind() != reflect.Slice {
    199 		return multiArgTypeInvalid, nil
    200 	}
    201 	if v.Type() == typeOfPropertyList {
    202 		return multiArgTypeInvalid, nil
    203 	}
    204 	elemType = v.Type().Elem()
    205 	if reflect.PtrTo(elemType).Implements(typeOfPropertyLoadSaver) {
    206 		return multiArgTypePropertyLoadSaver, elemType
    207 	}
    208 	switch elemType.Kind() {
    209 	case reflect.Struct:
    210 		return multiArgTypeStruct, elemType
    211 	case reflect.Interface:
    212 		return multiArgTypeInterface, elemType
    213 	case reflect.Ptr:
    214 		elemType = elemType.Elem()
    215 		if elemType.Kind() == reflect.Struct {
    216 			return multiArgTypeStructPtr, elemType
    217 		}
    218 	}
    219 	return multiArgTypeInvalid, nil
    220 }
    221 
    222 // Get loads the entity stored for k into dst, which must be a struct pointer
    223 // or implement PropertyLoadSaver. If there is no such entity for the key, Get
    224 // returns ErrNoSuchEntity.
    225 //
    226 // The values of dst's unmatched struct fields are not modified, and matching
    227 // slice-typed fields are not reset before appending to them. In particular, it
    228 // is recommended to pass a pointer to a zero valued struct on each Get call.
    229 //
    230 // ErrFieldMismatch is returned when a field is to be loaded into a different
    231 // type than the one it was stored from, or when a field is missing or
    232 // unexported in the destination struct. ErrFieldMismatch is only returned if
    233 // dst is a struct pointer.
    234 func Get(c context.Context, key *Key, dst interface{}) error {
    235 	if dst == nil { // GetMulti catches nil interface; we need to catch nil ptr here
    236 		return ErrInvalidEntityType
    237 	}
    238 	err := GetMulti(c, []*Key{key}, []interface{}{dst})
    239 	if me, ok := err.(appengine.MultiError); ok {
    240 		return me[0]
    241 	}
    242 	return err
    243 }
    244 
    245 // GetMulti is a batch version of Get.
    246 //
    247 // dst must be a []S, []*S, []I or []P, for some struct type S, some interface
    248 // type I, or some non-interface non-pointer type P such that P or *P
    249 // implements PropertyLoadSaver. If an []I, each element must be a valid dst
    250 // for Get: it must be a struct pointer or implement PropertyLoadSaver.
    251 //
    252 // As a special case, PropertyList is an invalid type for dst, even though a
    253 // PropertyList is a slice of structs. It is treated as invalid to avoid being
    254 // mistakenly passed when []PropertyList was intended.
    255 func GetMulti(c context.Context, key []*Key, dst interface{}) error {
    256 	v := reflect.ValueOf(dst)
    257 	multiArgType, _ := checkMultiArg(v)
    258 	if multiArgType == multiArgTypeInvalid {
    259 		return errors.New("datastore: dst has invalid type")
    260 	}
    261 	if len(key) != v.Len() {
    262 		return errors.New("datastore: key and dst slices have different length")
    263 	}
    264 	if len(key) == 0 {
    265 		return nil
    266 	}
    267 	if err := multiValid(key); err != nil {
    268 		return err
    269 	}
    270 	req := &pb.GetRequest{
    271 		Key: multiKeyToProto(internal.FullyQualifiedAppID(c), key),
    272 	}
    273 	res := &pb.GetResponse{}
    274 	if err := internal.Call(c, "datastore_v3", "Get", req, res); err != nil {
    275 		return err
    276 	}
    277 	if len(key) != len(res.Entity) {
    278 		return errors.New("datastore: internal error: server returned the wrong number of entities")
    279 	}
    280 	multiErr, any := make(appengine.MultiError, len(key)), false
    281 	for i, e := range res.Entity {
    282 		if e.Entity == nil {
    283 			multiErr[i] = ErrNoSuchEntity
    284 		} else {
    285 			elem := v.Index(i)
    286 			if multiArgType == multiArgTypePropertyLoadSaver || multiArgType == multiArgTypeStruct {
    287 				elem = elem.Addr()
    288 			}
    289 			if multiArgType == multiArgTypeStructPtr && elem.IsNil() {
    290 				elem.Set(reflect.New(elem.Type().Elem()))
    291 			}
    292 			multiErr[i] = loadEntity(elem.Interface(), e.Entity)
    293 		}
    294 		if multiErr[i] != nil {
    295 			any = true
    296 		}
    297 	}
    298 	if any {
    299 		return multiErr
    300 	}
    301 	return nil
    302 }
    303 
    304 // Put saves the entity src into the datastore with key k. src must be a struct
    305 // pointer or implement PropertyLoadSaver; if a struct pointer then any
    306 // unexported fields of that struct will be skipped. If k is an incomplete key,
    307 // the returned key will be a unique key generated by the datastore.
    308 func Put(c context.Context, key *Key, src interface{}) (*Key, error) {
    309 	k, err := PutMulti(c, []*Key{key}, []interface{}{src})
    310 	if err != nil {
    311 		if me, ok := err.(appengine.MultiError); ok {
    312 			return nil, me[0]
    313 		}
    314 		return nil, err
    315 	}
    316 	return k[0], nil
    317 }
    318 
    319 // PutMulti is a batch version of Put.
    320 //
    321 // src must satisfy the same conditions as the dst argument to GetMulti.
    322 func PutMulti(c context.Context, key []*Key, src interface{}) ([]*Key, error) {
    323 	v := reflect.ValueOf(src)
    324 	multiArgType, _ := checkMultiArg(v)
    325 	if multiArgType == multiArgTypeInvalid {
    326 		return nil, errors.New("datastore: src has invalid type")
    327 	}
    328 	if len(key) != v.Len() {
    329 		return nil, errors.New("datastore: key and src slices have different length")
    330 	}
    331 	if len(key) == 0 {
    332 		return nil, nil
    333 	}
    334 	appID := internal.FullyQualifiedAppID(c)
    335 	if err := multiValid(key); err != nil {
    336 		return nil, err
    337 	}
    338 	req := &pb.PutRequest{}
    339 	for i := range key {
    340 		elem := v.Index(i)
    341 		if multiArgType == multiArgTypePropertyLoadSaver || multiArgType == multiArgTypeStruct {
    342 			elem = elem.Addr()
    343 		}
    344 		sProto, err := saveEntity(appID, key[i], elem.Interface())
    345 		if err != nil {
    346 			return nil, err
    347 		}
    348 		req.Entity = append(req.Entity, sProto)
    349 	}
    350 	res := &pb.PutResponse{}
    351 	if err := internal.Call(c, "datastore_v3", "Put", req, res); err != nil {
    352 		return nil, err
    353 	}
    354 	if len(key) != len(res.Key) {
    355 		return nil, errors.New("datastore: internal error: server returned the wrong number of keys")
    356 	}
    357 	ret := make([]*Key, len(key))
    358 	for i := range ret {
    359 		var err error
    360 		ret[i], err = protoToKey(res.Key[i])
    361 		if err != nil || ret[i].Incomplete() {
    362 			return nil, errors.New("datastore: internal error: server returned an invalid key")
    363 		}
    364 	}
    365 	return ret, nil
    366 }
    367 
    368 // Delete deletes the entity for the given key.
    369 func Delete(c context.Context, key *Key) error {
    370 	err := DeleteMulti(c, []*Key{key})
    371 	if me, ok := err.(appengine.MultiError); ok {
    372 		return me[0]
    373 	}
    374 	return err
    375 }
    376 
    377 // DeleteMulti is a batch version of Delete.
    378 func DeleteMulti(c context.Context, key []*Key) error {
    379 	if len(key) == 0 {
    380 		return nil
    381 	}
    382 	if err := multiValid(key); err != nil {
    383 		return err
    384 	}
    385 	req := &pb.DeleteRequest{
    386 		Key: multiKeyToProto(internal.FullyQualifiedAppID(c), key),
    387 	}
    388 	res := &pb.DeleteResponse{}
    389 	return internal.Call(c, "datastore_v3", "Delete", req, res)
    390 }
    391 
    392 func namespaceMod(m proto.Message, namespace string) {
    393 	// pb.Query is the only type that has a name_space field.
    394 	// All other namespace support in datastore is in the keys.
    395 	switch m := m.(type) {
    396 	case *pb.Query:
    397 		if m.NameSpace == nil {
    398 			m.NameSpace = &namespace
    399 		}
    400 	}
    401 }
    402 
    403 func init() {
    404 	internal.NamespaceMods["datastore_v3"] = namespaceMod
    405 	internal.RegisterErrorCodeMap("datastore_v3", pb.Error_ErrorCode_name)
    406 	internal.RegisterTimeoutErrorCode("datastore_v3", int32(pb.Error_TIMEOUT))
    407 }
    408