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

Unified Diff: go/src/infra/gae/libs/gae/helper/datastore_impl.go

Issue 1227183003: Change RawDatastore to do less reflection. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@move_dummy
Patch Set: rebase 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 side-by-side diff with in-line comments
Download patch
Index: go/src/infra/gae/libs/gae/helper/datastore_impl.go
diff --git a/go/src/infra/gae/libs/gae/helper/datastore_impl.go b/go/src/infra/gae/libs/gae/helper/datastore_impl.go
index bfe066ea4871b9713e6ade287f3a284fe316d5e7..01a032e1530ab7e034c1b27bf0605b8c428abbf9 100644
--- a/go/src/infra/gae/libs/gae/helper/datastore_impl.go
+++ b/go/src/infra/gae/libs/gae/helper/datastore_impl.go
@@ -11,6 +11,7 @@ import (
"fmt"
"infra/gae/libs/gae"
Vadim Sh. 2015/07/14 18:55:43 nit: move into separate import block
iannucci 2015/07/14 22:45:48 Done.
"reflect"
+ "strconv"
"strings"
"sync"
"time"
@@ -25,6 +26,8 @@ var (
typeOfDSPropertyConverter = reflect.TypeOf((*gae.DSPropertyConverter)(nil)).Elem()
typeOfGeoPoint = reflect.TypeOf(gae.DSGeoPoint{})
typeOfTime = reflect.TypeOf(time.Time{})
+ typeOfString = reflect.TypeOf("")
+ typeOfInt64 = reflect.TypeOf(int64(0))
valueOfnilDSKey = reflect.Zero(typeOfDSKey)
)
@@ -35,15 +38,16 @@ type structTag struct {
isSlice bool
substructCodec *structCodec
convert bool
- specialVal string
+ metaVal interface{}
+ metaIsString bool
iannucci 2015/07/14 18:15:44 This metaIsString used to be useful... I'll take i
}
type structCodec struct {
- bySpecial map[string]int
- byName map[string]int
- byIndex []structTag
- hasSlice bool
- problem error
+ byMeta map[string]int
+ byName map[string]int
+ byIndex []structTag
+ hasSlice bool
+ problem error
}
type structPLS struct {
@@ -51,7 +55,7 @@ type structPLS struct {
c *structCodec
}
-var _ gae.DSStructPLS = (*structPLS)(nil)
+var _ gae.DSPropertyLoadSaver = (*structPLS)(nil)
// typeMismatchReason returns a string explaining why the property p could not
// be stored in an entity field of type v.Type().
@@ -60,22 +64,35 @@ func typeMismatchReason(val interface{}, v reflect.Value) string {
return fmt.Sprintf("type mismatch: %s versus %v", entityType, v.Type())
}
-func (p *structPLS) Load(propMap gae.DSPropertyMap) (convFailures []string, fatal error) {
- if fatal = p.Problem(); fatal != nil {
- return
+func (p *structPLS) Load(propMap gae.DSPropertyMap) error {
iannucci 2015/07/14 18:15:44 I changed this interface to only return one error,
+ if err := p.Problem(); err != nil {
+ return err
}
- t := p.o.Type()
+ convFailures := gae.MultiError(nil)
+
+ t := reflect.Type(nil)
for name, props := range propMap {
multiple := len(props) > 1
for i, prop := range props {
if reason := loadInner(p.c, p.o, i, name, prop, multiple); reason != "" {
- convFailures = append(convFailures, fmt.Sprintf(
- "cannot load field %q into a %q: %s", name, t, reason))
+ if t == nil {
+ t = p.o.Type()
+ }
+ convFailures = append(convFailures, &gae.ErrDSFieldMismatch{
+ StructType: t,
+ FieldName: name,
+ Reason: reason,
+ })
}
}
}
- return
+
+ if convFailures != nil {
+ return convFailures
+ }
+
+ return nil
}
func loadInner(codec *structCodec, structValue reflect.Value, index int, name string, p gae.DSProperty, requireSlice bool) string {
@@ -222,12 +239,29 @@ func loadInner(codec *structCodec, structValue reflect.Value, index int, name st
return ""
}
-func (p *structPLS) Save() (gae.DSPropertyMap, error) {
- ret := gae.DSPropertyMap{}
+func (p *structPLS) Save(withMeta bool) (gae.DSPropertyMap, error) {
iannucci 2015/07/14 18:15:44 withMeta was added so that the caller can ask for
+ size := len(p.c.byName)
+ if withMeta {
+ size += len(p.c.byMeta)
+ }
+ ret := make(gae.DSPropertyMap, size)
idxCount := 0
if err := p.save(ret, &idxCount, "", false); err != nil {
Vadim Sh. 2015/07/14 18:55:43 are you using pointers to return values from a fun
iannucci 2015/07/14 22:45:47 Yeah, I'm sorry... this is C+P from the SDK implem
return nil, err
}
+ if withMeta {
+ for k := range p.c.byMeta {
+ val, err := p.GetMeta(k)
+ if err != nil {
+ return nil, err // TODO(riannucci): should these be ignored?
+ }
+ p := gae.DSProperty{}
+ if err = p.SetValue(val, true); err != nil {
+ return nil, err
+ }
+ ret["$"+k] = []gae.DSProperty{p}
+ }
+ }
return ret, nil
}
@@ -285,35 +319,37 @@ func (p *structPLS) save(propMap gae.DSPropertyMap, idxCount *int, prefix string
return nil
}
-func (p *structPLS) GetSpecial(key string) (val string, current interface{}, err error) {
- if err = p.Problem(); err != nil {
- return
+func (p *structPLS) GetMeta(key string) (interface{}, error) {
iannucci 2015/07/14 18:15:43 "special" was renamed to "meta" to more clearly de
+ if err := p.Problem(); err != nil {
+ return nil, err
}
- idx, ok := p.c.bySpecial[key]
+ idx, ok := p.c.byMeta[key]
if !ok {
- err = gae.ErrDSSpecialFieldUnset
- return
+ return nil, gae.ErrDSMetaFieldUnset
}
- val = p.c.byIndex[idx].specialVal
+ st := p.c.byIndex[idx]
+ val := st.metaVal
f := p.o.Field(idx)
if f.CanSet() {
- current = f.Interface()
+ if !reflect.DeepEqual(reflect.Zero(f.Type()).Interface(), f.Interface()) {
+ val = f.Interface()
+ }
}
- return
+ return val, nil
}
-func (p *structPLS) SetSpecial(key string, val interface{}) (err error) {
+func (p *structPLS) SetMeta(key string, val interface{}) (err error) {
if err = p.Problem(); err != nil {
return
}
- idx, ok := p.c.bySpecial[key]
+ idx, ok := p.c.byMeta[key]
if !ok {
- return gae.ErrDSSpecialFieldUnset
+ return gae.ErrDSMetaFieldUnset
}
defer func() {
pv := recover()
Vadim Sh. 2015/07/14 18:55:43 nooooo!.. Is this the only way to catch errors her
dnj 2015/07/14 19:34:57 +1. IMO if some app is concerned with a panic in i
iannucci 2015/07/14 22:45:48 Fixed!
if pv != nil && err == nil {
- err = fmt.Errorf("gae/helper: cannot set special %q: %s", key, pv)
+ err = fmt.Errorf("gae/helper: cannot set meta %q: %s", key, pv)
}
}()
p.o.Field(idx).Set(reflect.ValueOf(val))
@@ -372,17 +408,17 @@ func getStructCodecLocked(t reflect.Type) (c *structCodec) {
}
c = &structCodec{
- byIndex: make([]structTag, t.NumField()),
- byName: make(map[string]int, t.NumField()),
- bySpecial: make(map[string]int, t.NumField()),
- problem: errRecursiveStruct, // we'll clear this later if it's not recursive
+ byIndex: make([]structTag, t.NumField()),
+ byName: make(map[string]int, t.NumField()),
+ byMeta: make(map[string]int, t.NumField()),
+ problem: errRecursiveStruct, // we'll clear this later if it's not recursive
}
defer func() {
// If the codec has a problem, free up the indexes
if c.problem != nil {
c.byIndex = nil
c.byName = nil
- c.bySpecial = nil
+ c.byMeta = nil
}
}()
structCodecs[t] = c
@@ -402,12 +438,18 @@ func getStructCodecLocked(t reflect.Type) (c *structCodec) {
}
case name[0] == '$':
name = name[1:]
- if _, ok := c.bySpecial[name]; ok {
- c.problem = me("special field %q set multiple times", "$"+name)
+ if _, ok := c.byMeta[name]; ok {
+ c.problem = me("meta field %q set multiple times", "$"+name)
return
}
- c.bySpecial[name] = i
- st.specialVal = opts
+ c.byMeta[name] = i
+ mv, err := convertMeta(opts, f.Type)
+ if err != nil {
+ c.problem = me("meta field %q has bad type: %s", "$"+name, err)
+ return
+ }
+ st.metaVal = mv
+ st.metaIsString = f.Type.Kind() == reflect.String
fallthrough
case name == "-":
st.name = "-"
@@ -507,3 +549,16 @@ func getStructCodecLocked(t reflect.Type) (c *structCodec) {
}
return
}
+
+func convertMeta(val string, t reflect.Type) (interface{}, error) {
+ switch t {
Vadim Sh. 2015/07/14 18:55:43 what about boolean?
iannucci 2015/07/14 22:45:48 oh, right. I said supported that, didn't I? :D I'm
+ case typeOfString:
+ return val, nil
+ case typeOfInt64:
+ if val == "" {
+ return int64(0), nil
+ }
+ return strconv.ParseInt(val, 10, 64)
+ }
+ return nil, fmt.Errorf("helper: meta field with bad type %s", t)
+}

Powered by Google App Engine
This is Rietveld 408576698