• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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