Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(165)

Side by Side Diff: go/src/infra/gae/libs/gae/properties.go

Issue 1227183003: Change RawDatastore to do less reflection. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@move_dummy
Patch Set: fix No/ShouldIndex Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package gae 5 package gae
6 6
7 import ( 7 import (
8 "errors" 8 "errors"
9 "fmt" 9 "fmt"
10 "math" 10 "math"
11 "reflect" 11 "reflect"
12 "time" 12 "time"
13 ) 13 )
14 14
15 var ( 15 var (
16 » // ErrDSSpecialFieldUnset is returned from DSStructPLS.{Get,Set}Special 16 » // ErrDSMetaFieldUnset is returned from DSPropertyLoadSaver.{Get,Set}Met a
17 » // implementations when the specified special key isn't set on the struc t at 17 » // implementations when the specified meta key isn't set on the struct a t
18 // all. 18 // all.
19 » ErrDSSpecialFieldUnset = fmt.Errorf("gae: special field unset") 19 » ErrDSMetaFieldUnset = fmt.Errorf("gae: meta field unset")
20 ) 20 )
21 21
22 var ( 22 var (
23 typeOfBSKey = reflect.TypeOf(BSKey("")) 23 typeOfBSKey = reflect.TypeOf(BSKey(""))
24 typeOfBool = reflect.TypeOf(false) 24 typeOfBool = reflect.TypeOf(false)
25 typeOfByteSlice = reflect.TypeOf([]byte(nil)) 25 typeOfByteSlice = reflect.TypeOf([]byte(nil))
26 typeOfDSByteString = reflect.TypeOf(DSByteString(nil)) 26 typeOfDSByteString = reflect.TypeOf(DSByteString(nil))
27 typeOfDSGeoPoint = reflect.TypeOf(DSGeoPoint{}) 27 typeOfDSGeoPoint = reflect.TypeOf(DSGeoPoint{})
28 typeOfDSKey = reflect.TypeOf((*DSKey)(nil)).Elem() 28 typeOfDSKey = reflect.TypeOf((*DSKey)(nil)).Elem()
29 typeOfFloat64 = reflect.TypeOf(float64(0)) 29 typeOfFloat64 = reflect.TypeOf(float64(0))
30 typeOfInt64 = reflect.TypeOf(int64(0)) 30 typeOfInt64 = reflect.TypeOf(int64(0))
31 typeOfString = reflect.TypeOf("") 31 typeOfString = reflect.TypeOf("")
32 typeOfTime = reflect.TypeOf(time.Time{}) 32 typeOfTime = reflect.TypeOf(time.Time{})
33 ) 33 )
34 34
35 var ( 35 var (
36 minTime = time.Unix(int64(math.MinInt64)/1e6, (int64(math.MinInt64)%1e6) *1e3) 36 minTime = time.Unix(int64(math.MinInt64)/1e6, (int64(math.MinInt64)%1e6) *1e3)
37 maxTime = time.Unix(int64(math.MaxInt64)/1e6, (int64(math.MaxInt64)%1e6) *1e3) 37 maxTime = time.Unix(int64(math.MaxInt64)/1e6, (int64(math.MaxInt64)%1e6) *1e3)
38 ) 38 )
39 39
40 type IndexSetting bool
41
42 const (
43 // ShouldIndex is the default, which is why it must assume the zero valu e,
44 // even though it's werid :(.
45 ShouldIndex IndexSetting = false
46 NoIndex IndexSetting = true
47 )
48
49 func (i IndexSetting) String() string {
50 if i {
51 return "NoIndex"
52 }
53 return "ShouldIndex"
54 }
55
40 // DSProperty is a value plus an indicator of whether the value should be 56 // DSProperty is a value plus an indicator of whether the value should be
41 // indexed. Name and Multiple are stored in the DSPropertyMap object. 57 // indexed. Name and Multiple are stored in the DSPropertyMap object.
42 type DSProperty struct { 58 type DSProperty struct {
43 » value interface{} 59 » value interface{}
44 » noIndex bool 60 » indexSetting IndexSetting
45 » propType DSPropertyType 61 » propType DSPropertyType
62 }
63
64 // MkDSProperty makes a new indexed* DSProperty and returns it. If val is an
65 // invalid value, this panics (so don't do it). If you want to handle the error
66 // normally, use SetValue(..., ShouldIndex) instead.
67 //
68 // *indexed if val is not an unindexable type like []byte.
69 func MkDSProperty(val interface{}) DSProperty {
70 » ret := DSProperty{}
71 » if err := ret.SetValue(val, ShouldIndex); err != nil {
72 » » panic(err)
73 » }
74 » return ret
75 }
76
77 // MkDSPropertyNI makes a new DSProperty (with noindex set to true), and returns
78 // it. If val is an invalid value, this panics (so don't do it). If you want to
79 // handle the error normally, use SetValue(..., NoIndex) instead.
80 func MkDSPropertyNI(val interface{}) DSProperty {
81 » ret := DSProperty{}
82 » if err := ret.SetValue(val, NoIndex); err != nil {
83 » » panic(err)
84 » }
85 » return ret
46 } 86 }
47 87
48 // DSPropertyConverter may be implemented by the pointer-to a struct field which 88 // DSPropertyConverter may be implemented by the pointer-to a struct field which
49 // is serialized by RawDatastore. Its ToDSProperty will be called on save, and 89 // is serialized by RawDatastore. Its ToDSProperty will be called on save, and
50 // it's FromDSProperty will be called on load (from datastore). The method may 90 // it's FromDSProperty will be called on load (from datastore). The method may
51 // do arbitrary computation, and if it encounters an error, may return it. This 91 // do arbitrary computation, and if it encounters an error, may return it. This
52 // error will be a fatal error (as defined by DSPropertyLoadSaver) for the 92 // error will be a fatal error (as defined by DSPropertyLoadSaver) for the
53 // struct conversion. 93 // struct conversion.
54 // 94 //
55 // Example: 95 // Example:
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after
229 } 269 }
230 } 270 }
231 return o, t 271 return o, t
232 } 272 }
233 273
234 // Value returns the current value held by this property. It's guaranteed to 274 // Value returns the current value held by this property. It's guaranteed to
235 // be a valid value type (i.e. `p.SetValue(p.Value(), true)` will never return 275 // be a valid value type (i.e. `p.SetValue(p.Value(), true)` will never return
236 // an error). 276 // an error).
237 func (p DSProperty) Value() interface{} { return p.value } 277 func (p DSProperty) Value() interface{} { return p.value }
238 278
239 // NoIndex says weather or not the datastore should create indicies for this 279 // IndexSetting says weather or not the datastore should create indicies for
240 // value. 280 // this value.
241 func (p DSProperty) NoIndex() bool { return p.noIndex } 281 func (p DSProperty) IndexSetting() IndexSetting { return p.indexSetting }
242 282
243 // Type is the DSPT* type of the data contained in Value(). 283 // Type is the DSPT* type of the data contained in Value().
244 func (p DSProperty) Type() DSPropertyType { return p.propType } 284 func (p DSProperty) Type() DSPropertyType { return p.propType }
245 285
246 // SetValue sets the Value field of a DSProperty, and ensures that its value 286 // SetValue sets the Value field of a DSProperty, and ensures that its value
247 // conforms to the permissible types. That way, you're guaranteed that if you 287 // conforms to the permissible types. That way, you're guaranteed that if you
248 // have a DSProperty, its value is valid. 288 // have a DSProperty, its value is valid.
249 // 289 //
250 // value is the property value. The valid types are: 290 // value is the property value. The valid types are:
251 // - int64 291 // - int64
(...skipping 12 matching lines...) Expand all
264 // must be explicitly on the list above; it is not sufficient for the 304 // must be explicitly on the list above; it is not sufficient for the
265 // underlying type to be on that list. For example, a Value of "type 305 // underlying type to be on that list. For example, a Value of "type
266 // myInt64 int64" is invalid. Smaller-width integers and floats are also 306 // myInt64 int64" is invalid. Smaller-width integers and floats are also
267 // invalid. Again, this is more restrictive than the set of valid struct 307 // invalid. Again, this is more restrictive than the set of valid struct
268 // field types. 308 // field types.
269 // 309 //
270 // A value may also be the nil interface value; this is equivalent to 310 // A value may also be the nil interface value; this is equivalent to
271 // Python's None but not directly representable by a Go struct. Loading 311 // Python's None but not directly representable by a Go struct. Loading
272 // a nil-valued property into a struct will set that field to the zero 312 // a nil-valued property into a struct will set that field to the zero
273 // value. 313 // value.
274 // 314 func (p *DSProperty) SetValue(value interface{}, is IndexSetting) (err error) {
275 // noIndex == false will attempt to set NoIndex to false as well. However, if
276 // value is an unindexable type, noIndex will be coerced to true automatically.
277 func (p *DSProperty) SetValue(value interface{}, noIndex bool) (err error) {
278 t := reflect.Type(nil) 315 t := reflect.Type(nil)
279 pt := DSPTNull 316 pt := DSPTNull
280 if value != nil { 317 if value != nil {
281 t = reflect.TypeOf(value) 318 t = reflect.TypeOf(value)
282 value, t = DSUpconvertUnderlyingType(value, t) 319 value, t = DSUpconvertUnderlyingType(value, t)
283 if pt, err = DSPropertyTypeOf(value, true); err != nil { 320 if pt, err = DSPropertyTypeOf(value, true); err != nil {
284 return 321 return
285 } 322 }
286 } 323 }
287 p.propType = pt 324 p.propType = pt
288 p.value = value 325 p.value = value
289 » p.noIndex = noIndex || t == typeOfByteSlice 326 » p.indexSetting = is
327 » if t == typeOfByteSlice {
328 » » p.indexSetting = NoIndex
329 » }
290 return 330 return
291 } 331 }
292 332
293 // DSPropertyLoadSaver may be implemented by a user type, and RawDatastore will 333 // DSPropertyLoadSaver may be implemented by a user type, and RawDatastore will
294 // use this interface to serialize the type instead of trying to automatically 334 // use this interface to serialize the type instead of trying to automatically
295 // create a serialization codec for it with helper.GetStructPLS. 335 // create a serialization codec for it with helper.GetPLS.
296 type DSPropertyLoadSaver interface { 336 type DSPropertyLoadSaver interface {
297 » Load(DSPropertyMap) (convFailures []string, fatal error) 337 » // Load takes the values from the given map and attempts to save them in to
298 » Save() (DSPropertyMap, error) 338 » // the underlying object (usually a struct or a DSPropertyMap). If a fat al
299 } 339 » // error occurs, it's returned via error. If non-fatal conversion errors
340 » // occur, error will be a MultiError containing one or more ErrDSFieldMi smatch
341 » // objects.
342 » Load(DSPropertyMap) error
300 343
301 // DSStructPLS is a DSPropertyLoadSaver, but with some bonus features which only 344 » // Save returns the current property as a DSPropertyMap. if withMeta is true,
302 // apply to user structs (instead of raw DSPropertyMap's). 345 » // then the DSPropertyMap contains all the metadata (e.g. '$meta' fields )
303 type DSStructPLS interface { 346 » // which was held by this DSPropertyLoadSaver.
304 » DSPropertyLoadSaver 347 » Save(withMeta bool) (DSPropertyMap, error)
305 348
306 » // GetSpecial will get information about the struct field which has the 349 » // GetMeta will get information about the field which has the struct tag in
307 » // struct tag in the form of `gae:"$<key>"`. 350 » // the form of `gae:"$<key>[,<value>]?"`.
351 » //
352 » // string and int64 fields will return the <value> in the struct tag,
353 » // converted to the appropriate type, if the field has the zero value.
308 // 354 //
309 // Example: 355 // Example:
310 // type MyStruct struct { 356 // type MyStruct struct {
311 » // CoolField int `gae:"$id,cool"` 357 » // CoolField int64 `gae:"$id,1"`
312 // } 358 // }
313 » // val, current, err := helper.GetStructPLS(&MyStruct{10}).GetSpecial( "id") 359 » // val, err := helper.GetPLS(&MyStruct{}).GetMeta("id")
314 » // // val == "cool" 360 » // // val == 1
315 » // // current == 10
316 // // err == nil 361 // // err == nil
317 » GetSpecial(key string) (val string, current interface{}, err error) 362 » //
363 » // val, err := helper.GetPLS(&MyStruct{10}).GetMeta("id")
364 » // // val == 10
365 » // // err == nil
366 » GetMeta(key string) (interface{}, error)
318 367
319 » // SetSpecial allows you to set the current value of the special-keyed f ield. 368 » // SetMeta allows you to set the current value of the meta-keyed field.
320 » SetSpecial(key string, val interface{}) error 369 » SetMeta(key string, val interface{}) error
321 370
322 » // Problem indicates that this StructPLS has a fatal problem. Usually th is is 371 » // Problem indicates that this PLS has a fatal problem. Usually this is
323 // set when the underlying struct has recursion, invalid field types, ne sted 372 // set when the underlying struct has recursion, invalid field types, ne sted
324 // slices, etc. 373 // slices, etc.
325 Problem() error 374 Problem() error
326 } 375 }
327 376
328 // DSPropertyMap represents the contents of a datastore entity in a generic way. 377 // DSPropertyMap represents the contents of a datastore entity in a generic way.
329 // It maps from property name to a list of property values which correspond to 378 // It maps from property name to a list of property values which correspond to
330 // that property name. It is the spiritual successor to PropertyList from the 379 // that property name. It is the spiritual successor to PropertyList from the
331 // original SDK. 380 // original SDK.
381 //
382 // DSPropertyMap may contain "meta" values, which are keyed with a '$' prefix.
383 // Technically the datastore allows arbitrary property names, but all of the
384 // SDKs go out of their way to try to make all property names valid programming
385 // language tokens. Special values must correspond to a single DSProperty...
386 // corresponding to 0 is equivalent to unset, and corresponding to >1 is an
387 // error. So:
388 //
389 // {
390 // "$id": {MkDSProperty(1)}, // GetProperty("id") -> 1, nil
391 // "$foo": {}, // GetProperty("foo") -> nil, ErrDSMetaFieldUnset
392 // // GetProperty("bar") -> nil, ErrDSMetaFieldUnset
393 // "$meep": {
394 // MkDSProperty("hi"),
395 // MkDSProperty("there")}, // GetProperty("meep") -> nil, error!
396 // }
397 //
398 // Additionally, Save returns a copy of the map with the meta keys omitted (e.g.
399 // these keys are not going to be serialized to the datastore).
332 type DSPropertyMap map[string][]DSProperty 400 type DSPropertyMap map[string][]DSProperty
333 401
334 var _ DSPropertyLoadSaver = (*DSPropertyMap)(nil) 402 var _ DSPropertyLoadSaver = DSPropertyMap(nil)
335 403
336 // Load implements DSPropertyLoadSaver.Load 404 // Load implements DSPropertyLoadSaver.Load
337 func (pm *DSPropertyMap) Load(props DSPropertyMap) (convErr []string, err error) { 405 func (pm DSPropertyMap) Load(props DSPropertyMap) error {
338 » if pm == nil { 406 » for k, v := range props {
339 » » return nil, errors.New("gae: nil DSPropertyMap") 407 » » pm[k] = append(pm[k], v...)
340 } 408 }
341 » if *pm == nil { 409 » return nil
342 » » *pm = make(DSPropertyMap, len(props))
343 » }
344 » for k, v := range props {
345 » » (*pm)[k] = append((*pm)[k], v...)
346 » }
347 » return nil, nil
348 } 410 }
349 411
350 // Save implements DSPropertyLoadSaver.Save 412 // Save implements DSPropertyLoadSaver.Save by returning a copy of the
351 func (pm *DSPropertyMap) Save() (DSPropertyMap, error) { 413 // current map data.
352 » if pm == nil { 414 func (pm DSPropertyMap) Save(withMeta bool) (DSPropertyMap, error) {
353 » » return nil, errors.New("gae: nil DSPropertyMap") 415 » if len(pm) == 0 {
416 » » return DSPropertyMap{}, nil
354 } 417 }
355 » return *pm, nil 418 » ret := make(DSPropertyMap, len(pm))
419 » for k, v := range pm {
420 » » if withMeta || len(k) == 0 || k[0] != '$' {
421 » » » ret[k] = append(ret[k], v...)
422 » » }
423 » }
424 » return ret, nil
356 } 425 }
426
427 func (pm DSPropertyMap) GetMeta(key string) (interface{}, error) {
428 v, ok := pm["$"+key]
429 if !ok || len(v) == 0 {
430 return nil, ErrDSMetaFieldUnset
431 }
432 if len(v) > 1 {
433 return nil, errors.New("gae: too many values for Meta key")
434 }
435 return v[0].Value(), nil
436 }
437
438 func (pm DSPropertyMap) SetMeta(key string, val interface{}) error {
439 prop := DSProperty{}
440 if err := prop.SetValue(val, NoIndex); err != nil {
441 return err
442 }
443 pm["$"+key] = []DSProperty{prop}
444 return nil
445 }
446
447 func (pm DSPropertyMap) Problem() error {
448 return nil
449 }
OLDNEW
« no previous file with comments | « go/src/infra/gae/libs/gae/prod/raw_datastore_type_converter.go ('k') | go/src/infra/gae/libs/gae/properties_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698