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