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 5package datastore 6 7import ( 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 20var ( 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. 35type ErrFieldMismatch struct { 36 StructType reflect.Type 37 FieldName string 38 Reason string 39} 40 41func (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. 48func 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. 68func 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. 105func 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. 115func 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. 141func 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. 162func 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 179type multiArgType int 180 181const ( 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. 197func 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. 234func 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. 255func 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. 308func 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. 322func 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. 369func 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. 378func 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 392func 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 403func 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