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	"fmt"
9	"reflect"
10	"strings"
11	"sync"
12	"unicode"
13)
14
15// Entities with more than this many indexed properties will not be saved.
16const maxIndexedProperties = 20000
17
18// []byte fields more than 1 megabyte long will not be loaded or saved.
19const maxBlobLen = 1 << 20
20
21// Property is a name/value pair plus some metadata. A datastore entity's
22// contents are loaded and saved as a sequence of Properties. An entity can
23// have multiple Properties with the same name, provided that p.Multiple is
24// true on all of that entity's Properties with that name.
25type Property struct {
26	// Name is the property name.
27	Name string
28	// Value is the property value. The valid types are:
29	//	- int64
30	//	- bool
31	//	- string
32	//	- float64
33	//	- ByteString
34	//	- *Key
35	//	- time.Time
36	//	- appengine.BlobKey
37	//	- appengine.GeoPoint
38	//	- []byte (up to 1 megabyte in length)
39	//	- *Entity (representing a nested struct)
40	// This set is smaller than the set of valid struct field types that the
41	// datastore can load and save. A Property Value cannot be a slice (apart
42	// from []byte); use multiple Properties instead. Also, a Value's type
43	// must be explicitly on the list above; it is not sufficient for the
44	// underlying type to be on that list. For example, a Value of "type
45	// myInt64 int64" is invalid. Smaller-width integers and floats are also
46	// invalid. Again, this is more restrictive than the set of valid struct
47	// field types.
48	//
49	// A Value will have an opaque type when loading entities from an index,
50	// such as via a projection query. Load entities into a struct instead
51	// of a PropertyLoadSaver when using a projection query.
52	//
53	// A Value may also be the nil interface value; this is equivalent to
54	// Python's None but not directly representable by a Go struct. Loading
55	// a nil-valued property into a struct will set that field to the zero
56	// value.
57	Value interface{}
58	// NoIndex is whether the datastore cannot index this property.
59	NoIndex bool
60	// Multiple is whether the entity can have multiple properties with
61	// the same name. Even if a particular instance only has one property with
62	// a certain name, Multiple should be true if a struct would best represent
63	// it as a field of type []T instead of type T.
64	Multiple bool
65}
66
67// An Entity is the value type for a nested struct.
68// This type is only used for a Property's Value.
69type Entity struct {
70	Key        *Key
71	Properties []Property
72}
73
74// ByteString is a short byte slice (up to 1500 bytes) that can be indexed.
75type ByteString []byte
76
77// PropertyLoadSaver can be converted from and to a slice of Properties.
78type PropertyLoadSaver interface {
79	Load([]Property) error
80	Save() ([]Property, error)
81}
82
83// PropertyList converts a []Property to implement PropertyLoadSaver.
84type PropertyList []Property
85
86var (
87	typeOfPropertyLoadSaver = reflect.TypeOf((*PropertyLoadSaver)(nil)).Elem()
88	typeOfPropertyList      = reflect.TypeOf(PropertyList(nil))
89)
90
91// Load loads all of the provided properties into l.
92// It does not first reset *l to an empty slice.
93func (l *PropertyList) Load(p []Property) error {
94	*l = append(*l, p...)
95	return nil
96}
97
98// Save saves all of l's properties as a slice or Properties.
99func (l *PropertyList) Save() ([]Property, error) {
100	return *l, nil
101}
102
103// validPropertyName returns whether name consists of one or more valid Go
104// identifiers joined by ".".
105func validPropertyName(name string) bool {
106	if name == "" {
107		return false
108	}
109	for _, s := range strings.Split(name, ".") {
110		if s == "" {
111			return false
112		}
113		first := true
114		for _, c := range s {
115			if first {
116				first = false
117				if c != '_' && !unicode.IsLetter(c) {
118					return false
119				}
120			} else {
121				if c != '_' && !unicode.IsLetter(c) && !unicode.IsDigit(c) {
122					return false
123				}
124			}
125		}
126	}
127	return true
128}
129
130// structCodec describes how to convert a struct to and from a sequence of
131// properties.
132type structCodec struct {
133	// fields gives the field codec for the structTag with the given name.
134	fields map[string]fieldCodec
135	// hasSlice is whether a struct or any of its nested or embedded structs
136	// has a slice-typed field (other than []byte).
137	hasSlice bool
138	// keyField is the index of a *Key field with structTag __key__.
139	// This field is not relevant for the top level struct, only for
140	// nested structs.
141	keyField int
142	// complete is whether the structCodec is complete. An incomplete
143	// structCodec may be encountered when walking a recursive struct.
144	complete bool
145}
146
147// fieldCodec is a struct field's index and, if that struct field's type is
148// itself a struct, that substruct's structCodec.
149type fieldCodec struct {
150	// path is the index path to the field
151	path    []int
152	noIndex bool
153	// omitEmpty indicates that the field should be omitted on save
154	// if empty.
155	omitEmpty bool
156	// structCodec is the codec fot the struct field at index 'path',
157	// or nil if the field is not a struct.
158	structCodec *structCodec
159}
160
161// structCodecs collects the structCodecs that have already been calculated.
162var (
163	structCodecsMutex sync.Mutex
164	structCodecs      = make(map[reflect.Type]*structCodec)
165)
166
167// getStructCodec returns the structCodec for the given struct type.
168func getStructCodec(t reflect.Type) (*structCodec, error) {
169	structCodecsMutex.Lock()
170	defer structCodecsMutex.Unlock()
171	return getStructCodecLocked(t)
172}
173
174// getStructCodecLocked implements getStructCodec. The structCodecsMutex must
175// be held when calling this function.
176func getStructCodecLocked(t reflect.Type) (ret *structCodec, retErr error) {
177	c, ok := structCodecs[t]
178	if ok {
179		return c, nil
180	}
181	c = &structCodec{
182		fields: make(map[string]fieldCodec),
183		// We initialize keyField to -1 so that the zero-value is not
184		// misinterpreted as index 0.
185		keyField: -1,
186	}
187
188	// Add c to the structCodecs map before we are sure it is good. If t is
189	// a recursive type, it needs to find the incomplete entry for itself in
190	// the map.
191	structCodecs[t] = c
192	defer func() {
193		if retErr != nil {
194			delete(structCodecs, t)
195		}
196	}()
197
198	for i := 0; i < t.NumField(); i++ {
199		f := t.Field(i)
200		// Skip unexported fields.
201		// Note that if f is an anonymous, unexported struct field,
202		// we will promote its fields.
203		if f.PkgPath != "" && !f.Anonymous {
204			continue
205		}
206
207		tags := strings.Split(f.Tag.Get("datastore"), ",")
208		name := tags[0]
209		opts := make(map[string]bool)
210		for _, t := range tags[1:] {
211			opts[t] = true
212		}
213		switch {
214		case name == "":
215			if !f.Anonymous {
216				name = f.Name
217			}
218		case name == "-":
219			continue
220		case name == "__key__":
221			if f.Type != typeOfKeyPtr {
222				return nil, fmt.Errorf("datastore: __key__ field on struct %v is not a *datastore.Key", t)
223			}
224			c.keyField = i
225		case !validPropertyName(name):
226			return nil, fmt.Errorf("datastore: struct tag has invalid property name: %q", name)
227		}
228
229		substructType, fIsSlice := reflect.Type(nil), false
230		switch f.Type.Kind() {
231		case reflect.Struct:
232			substructType = f.Type
233		case reflect.Slice:
234			if f.Type.Elem().Kind() == reflect.Struct {
235				substructType = f.Type.Elem()
236			}
237			fIsSlice = f.Type != typeOfByteSlice
238			c.hasSlice = c.hasSlice || fIsSlice
239		}
240
241		var sub *structCodec
242		if substructType != nil && substructType != typeOfTime && substructType != typeOfGeoPoint {
243			var err error
244			sub, err = getStructCodecLocked(substructType)
245			if err != nil {
246				return nil, err
247			}
248			if !sub.complete {
249				return nil, fmt.Errorf("datastore: recursive struct: field %q", f.Name)
250			}
251			if fIsSlice && sub.hasSlice {
252				return nil, fmt.Errorf(
253					"datastore: flattening nested structs leads to a slice of slices: field %q", f.Name)
254			}
255			c.hasSlice = c.hasSlice || sub.hasSlice
256			// If f is an anonymous struct field, we promote the substruct's fields up to this level
257			// in the linked list of struct codecs.
258			if f.Anonymous {
259				for subname, subfield := range sub.fields {
260					if name != "" {
261						subname = name + "." + subname
262					}
263					if _, ok := c.fields[subname]; ok {
264						return nil, fmt.Errorf("datastore: struct tag has repeated property name: %q", subname)
265					}
266					c.fields[subname] = fieldCodec{
267						path:        append([]int{i}, subfield.path...),
268						noIndex:     subfield.noIndex || opts["noindex"],
269						omitEmpty:   subfield.omitEmpty,
270						structCodec: subfield.structCodec,
271					}
272				}
273				continue
274			}
275		}
276
277		if _, ok := c.fields[name]; ok {
278			return nil, fmt.Errorf("datastore: struct tag has repeated property name: %q", name)
279		}
280		c.fields[name] = fieldCodec{
281			path:        []int{i},
282			noIndex:     opts["noindex"],
283			omitEmpty:   opts["omitempty"],
284			structCodec: sub,
285		}
286	}
287	c.complete = true
288	return c, nil
289}
290
291// structPLS adapts a struct to be a PropertyLoadSaver.
292type structPLS struct {
293	v     reflect.Value
294	codec *structCodec
295}
296
297// newStructPLS returns a structPLS, which implements the
298// PropertyLoadSaver interface, for the struct pointer p.
299func newStructPLS(p interface{}) (*structPLS, error) {
300	v := reflect.ValueOf(p)
301	if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
302		return nil, ErrInvalidEntityType
303	}
304	v = v.Elem()
305	codec, err := getStructCodec(v.Type())
306	if err != nil {
307		return nil, err
308	}
309	return &structPLS{v, codec}, nil
310}
311
312// LoadStruct loads the properties from p to dst.
313// dst must be a struct pointer.
314func LoadStruct(dst interface{}, p []Property) error {
315	x, err := newStructPLS(dst)
316	if err != nil {
317		return err
318	}
319	return x.Load(p)
320}
321
322// SaveStruct returns the properties from src as a slice of Properties.
323// src must be a struct pointer.
324func SaveStruct(src interface{}) ([]Property, error) {
325	x, err := newStructPLS(src)
326	if err != nil {
327		return nil, err
328	}
329	return x.Save()
330}
331