324 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			324 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Go
		
	
	
	
| // Copyright 2011 Google Inc. All rights reserved.
 | |
| // Use of this source code is governed by the Apache 2.0
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| package datastore
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| 	"strings"
 | |
| 	"sync"
 | |
| 	"unicode"
 | |
| )
 | |
| 
 | |
| // Entities with more than this many indexed properties will not be saved.
 | |
| const maxIndexedProperties = 20000
 | |
| 
 | |
| // []byte fields more than 1 megabyte long will not be loaded or saved.
 | |
| const maxBlobLen = 1 << 20
 | |
| 
 | |
| // Property is a name/value pair plus some metadata. A datastore entity's
 | |
| // contents are loaded and saved as a sequence of Properties. An entity can
 | |
| // have multiple Properties with the same name, provided that p.Multiple is
 | |
| // true on all of that entity's Properties with that name.
 | |
| type Property struct {
 | |
| 	// Name is the property name.
 | |
| 	Name string
 | |
| 	// Value is the property value. The valid types are:
 | |
| 	//	- int64
 | |
| 	//	- bool
 | |
| 	//	- string
 | |
| 	//	- float64
 | |
| 	//	- ByteString
 | |
| 	//	- *Key
 | |
| 	//	- time.Time
 | |
| 	//	- appengine.BlobKey
 | |
| 	//	- appengine.GeoPoint
 | |
| 	//	- []byte (up to 1 megabyte in length)
 | |
| 	//	- *Entity (representing a nested struct)
 | |
| 	// This set is smaller than the set of valid struct field types that the
 | |
| 	// datastore can load and save. A Property Value cannot be a slice (apart
 | |
| 	// from []byte); use multiple Properties instead. Also, a Value's type
 | |
| 	// must be explicitly on the list above; it is not sufficient for the
 | |
| 	// underlying type to be on that list. For example, a Value of "type
 | |
| 	// myInt64 int64" is invalid. Smaller-width integers and floats are also
 | |
| 	// invalid. Again, this is more restrictive than the set of valid struct
 | |
| 	// field types.
 | |
| 	//
 | |
| 	// A Value will have an opaque type when loading entities from an index,
 | |
| 	// such as via a projection query. Load entities into a struct instead
 | |
| 	// of a PropertyLoadSaver when using a projection query.
 | |
| 	//
 | |
| 	// A Value may also be the nil interface value; this is equivalent to
 | |
| 	// Python's None but not directly representable by a Go struct. Loading
 | |
| 	// a nil-valued property into a struct will set that field to the zero
 | |
| 	// value.
 | |
| 	Value interface{}
 | |
| 	// NoIndex is whether the datastore cannot index this property.
 | |
| 	NoIndex bool
 | |
| 	// Multiple is whether the entity can have multiple properties with
 | |
| 	// the same name. Even if a particular instance only has one property with
 | |
| 	// a certain name, Multiple should be true if a struct would best represent
 | |
| 	// it as a field of type []T instead of type T.
 | |
| 	Multiple bool
 | |
| }
 | |
| 
 | |
| // An Entity is the value type for a nested struct.
 | |
| // This type is only used for a Property's Value.
 | |
| type Entity struct {
 | |
| 	Key        *Key
 | |
| 	Properties []Property
 | |
| }
 | |
| 
 | |
| // ByteString is a short byte slice (up to 1500 bytes) that can be indexed.
 | |
| type ByteString []byte
 | |
| 
 | |
| // PropertyLoadSaver can be converted from and to a slice of Properties.
 | |
| type PropertyLoadSaver interface {
 | |
| 	Load([]Property) error
 | |
| 	Save() ([]Property, error)
 | |
| }
 | |
| 
 | |
| // PropertyList converts a []Property to implement PropertyLoadSaver.
 | |
| type PropertyList []Property
 | |
| 
 | |
| var (
 | |
| 	typeOfPropertyLoadSaver = reflect.TypeOf((*PropertyLoadSaver)(nil)).Elem()
 | |
| 	typeOfPropertyList      = reflect.TypeOf(PropertyList(nil))
 | |
| )
 | |
| 
 | |
| // Load loads all of the provided properties into l.
 | |
| // It does not first reset *l to an empty slice.
 | |
| func (l *PropertyList) Load(p []Property) error {
 | |
| 	*l = append(*l, p...)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Save saves all of l's properties as a slice or Properties.
 | |
| func (l *PropertyList) Save() ([]Property, error) {
 | |
| 	return *l, nil
 | |
| }
 | |
| 
 | |
| // validPropertyName returns whether name consists of one or more valid Go
 | |
| // identifiers joined by ".".
 | |
| func validPropertyName(name string) bool {
 | |
| 	if name == "" {
 | |
| 		return false
 | |
| 	}
 | |
| 	for _, s := range strings.Split(name, ".") {
 | |
| 		if s == "" {
 | |
| 			return false
 | |
| 		}
 | |
| 		first := true
 | |
| 		for _, c := range s {
 | |
| 			if first {
 | |
| 				first = false
 | |
| 				if c != '_' && !unicode.IsLetter(c) {
 | |
| 					return false
 | |
| 				}
 | |
| 			} else {
 | |
| 				if c != '_' && !unicode.IsLetter(c) && !unicode.IsDigit(c) {
 | |
| 					return false
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // structCodec describes how to convert a struct to and from a sequence of
 | |
| // properties.
 | |
| type structCodec struct {
 | |
| 	// fields gives the field codec for the structTag with the given name.
 | |
| 	fields map[string]fieldCodec
 | |
| 	// hasSlice is whether a struct or any of its nested or embedded structs
 | |
| 	// has a slice-typed field (other than []byte).
 | |
| 	hasSlice bool
 | |
| 	// keyField is the index of a *Key field with structTag __key__.
 | |
| 	// This field is not relevant for the top level struct, only for
 | |
| 	// nested structs.
 | |
| 	keyField int
 | |
| 	// complete is whether the structCodec is complete. An incomplete
 | |
| 	// structCodec may be encountered when walking a recursive struct.
 | |
| 	complete bool
 | |
| }
 | |
| 
 | |
| // fieldCodec is a struct field's index and, if that struct field's type is
 | |
| // itself a struct, that substruct's structCodec.
 | |
| type fieldCodec struct {
 | |
| 	// path is the index path to the field
 | |
| 	path    []int
 | |
| 	noIndex bool
 | |
| 	// structCodec is the codec fot the struct field at index 'path',
 | |
| 	// or nil if the field is not a struct.
 | |
| 	structCodec *structCodec
 | |
| }
 | |
| 
 | |
| // structCodecs collects the structCodecs that have already been calculated.
 | |
| var (
 | |
| 	structCodecsMutex sync.Mutex
 | |
| 	structCodecs      = make(map[reflect.Type]*structCodec)
 | |
| )
 | |
| 
 | |
| // getStructCodec returns the structCodec for the given struct type.
 | |
| func getStructCodec(t reflect.Type) (*structCodec, error) {
 | |
| 	structCodecsMutex.Lock()
 | |
| 	defer structCodecsMutex.Unlock()
 | |
| 	return getStructCodecLocked(t)
 | |
| }
 | |
| 
 | |
| // getStructCodecLocked implements getStructCodec. The structCodecsMutex must
 | |
| // be held when calling this function.
 | |
| func getStructCodecLocked(t reflect.Type) (ret *structCodec, retErr error) {
 | |
| 	c, ok := structCodecs[t]
 | |
| 	if ok {
 | |
| 		return c, nil
 | |
| 	}
 | |
| 	c = &structCodec{
 | |
| 		fields: make(map[string]fieldCodec),
 | |
| 		// We initialize keyField to -1 so that the zero-value is not
 | |
| 		// misinterpreted as index 0.
 | |
| 		keyField: -1,
 | |
| 	}
 | |
| 
 | |
| 	// Add c to the structCodecs map before we are sure it is good. If t is
 | |
| 	// a recursive type, it needs to find the incomplete entry for itself in
 | |
| 	// the map.
 | |
| 	structCodecs[t] = c
 | |
| 	defer func() {
 | |
| 		if retErr != nil {
 | |
| 			delete(structCodecs, t)
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	for i := 0; i < t.NumField(); i++ {
 | |
| 		f := t.Field(i)
 | |
| 		// Skip unexported fields.
 | |
| 		// Note that if f is an anonymous, unexported struct field,
 | |
| 		// we will not promote its fields. We will skip f entirely.
 | |
| 		if f.PkgPath != "" {
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		tags := strings.Split(f.Tag.Get("datastore"), ",")
 | |
| 		name := tags[0]
 | |
| 		opts := make(map[string]bool)
 | |
| 		for _, t := range tags[1:] {
 | |
| 			opts[t] = true
 | |
| 		}
 | |
| 		switch {
 | |
| 		case name == "":
 | |
| 			if !f.Anonymous {
 | |
| 				name = f.Name
 | |
| 			}
 | |
| 		case name == "-":
 | |
| 			continue
 | |
| 		case name == "__key__":
 | |
| 			if f.Type != typeOfKeyPtr {
 | |
| 				return nil, fmt.Errorf("datastore: __key__ field on struct %v is not a *datastore.Key", t)
 | |
| 			}
 | |
| 			c.keyField = i
 | |
| 		case !validPropertyName(name):
 | |
| 			return nil, fmt.Errorf("datastore: struct tag has invalid property name: %q", name)
 | |
| 		}
 | |
| 
 | |
| 		substructType, fIsSlice := reflect.Type(nil), false
 | |
| 		switch f.Type.Kind() {
 | |
| 		case reflect.Struct:
 | |
| 			substructType = f.Type
 | |
| 		case reflect.Slice:
 | |
| 			if f.Type.Elem().Kind() == reflect.Struct {
 | |
| 				substructType = f.Type.Elem()
 | |
| 			}
 | |
| 			fIsSlice = f.Type != typeOfByteSlice
 | |
| 			c.hasSlice = c.hasSlice || fIsSlice
 | |
| 		}
 | |
| 
 | |
| 		var sub *structCodec
 | |
| 		if substructType != nil && substructType != typeOfTime && substructType != typeOfGeoPoint {
 | |
| 			var err error
 | |
| 			sub, err = getStructCodecLocked(substructType)
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			if !sub.complete {
 | |
| 				return nil, fmt.Errorf("datastore: recursive struct: field %q", f.Name)
 | |
| 			}
 | |
| 			if fIsSlice && sub.hasSlice {
 | |
| 				return nil, fmt.Errorf(
 | |
| 					"datastore: flattening nested structs leads to a slice of slices: field %q", f.Name)
 | |
| 			}
 | |
| 			c.hasSlice = c.hasSlice || sub.hasSlice
 | |
| 			// If name is empty at this point, f is an anonymous struct field.
 | |
| 			// In this case, we promote the substruct's fields up to this level
 | |
| 			// in the linked list of struct codecs.
 | |
| 			if name == "" {
 | |
| 				for subname, subfield := range sub.fields {
 | |
| 					if _, ok := c.fields[subname]; ok {
 | |
| 						return nil, fmt.Errorf("datastore: struct tag has repeated property name: %q", subname)
 | |
| 					}
 | |
| 					c.fields[subname] = fieldCodec{
 | |
| 						path:        append([]int{i}, subfield.path...),
 | |
| 						noIndex:     subfield.noIndex || opts["noindex"],
 | |
| 						structCodec: subfield.structCodec,
 | |
| 					}
 | |
| 				}
 | |
| 				continue
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if _, ok := c.fields[name]; ok {
 | |
| 			return nil, fmt.Errorf("datastore: struct tag has repeated property name: %q", name)
 | |
| 		}
 | |
| 		c.fields[name] = fieldCodec{
 | |
| 			path:        []int{i},
 | |
| 			noIndex:     opts["noindex"],
 | |
| 			structCodec: sub,
 | |
| 		}
 | |
| 	}
 | |
| 	c.complete = true
 | |
| 	return c, nil
 | |
| }
 | |
| 
 | |
| // structPLS adapts a struct to be a PropertyLoadSaver.
 | |
| type structPLS struct {
 | |
| 	v     reflect.Value
 | |
| 	codec *structCodec
 | |
| }
 | |
| 
 | |
| // newStructPLS returns a structPLS, which implements the
 | |
| // PropertyLoadSaver interface, for the struct pointer p.
 | |
| func newStructPLS(p interface{}) (*structPLS, error) {
 | |
| 	v := reflect.ValueOf(p)
 | |
| 	if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
 | |
| 		return nil, ErrInvalidEntityType
 | |
| 	}
 | |
| 	v = v.Elem()
 | |
| 	codec, err := getStructCodec(v.Type())
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return &structPLS{v, codec}, nil
 | |
| }
 | |
| 
 | |
| // LoadStruct loads the properties from p to dst.
 | |
| // dst must be a struct pointer.
 | |
| func LoadStruct(dst interface{}, p []Property) error {
 | |
| 	x, err := newStructPLS(dst)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return x.Load(p)
 | |
| }
 | |
| 
 | |
| // SaveStruct returns the properties from src as a slice of Properties.
 | |
| // src must be a struct pointer.
 | |
| func SaveStruct(src interface{}) ([]Property, error) {
 | |
| 	x, err := newStructPLS(src)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return x.Save()
 | |
| }
 |