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