github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/google.golang.org/appengine/datastore/datastore.go (about)

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