Index: impl/prod/raw_datastore_type_converter.go |
diff --git a/impl/prod/raw_datastore_type_converter.go b/impl/prod/raw_datastore_type_converter.go |
index 0543326492c450ebe6e665b180d17ed741b82f1c..5df7d355525038c593c648e1e5dec9534b0dd59f 100644 |
--- a/impl/prod/raw_datastore_type_converter.go |
+++ b/impl/prod/raw_datastore_type_converter.go |
@@ -5,10 +5,13 @@ |
package prod |
import ( |
+ "fmt" |
+ "reflect" |
"time" |
bs "github.com/luci/gae/service/blobstore" |
ds "github.com/luci/gae/service/datastore" |
+ "github.com/luci/gae/service/datastore/dskey" |
"google.golang.org/appengine" |
"google.golang.org/appengine/datastore" |
) |
@@ -19,13 +22,89 @@ type typeFilter struct { |
var _ datastore.PropertyLoadSaver = &typeFilter{} |
+func maybeIndexValue(val interface{}) interface{} { |
+ // It may be the SDK's datastore.indexValue structure (in datastore/load.go). |
+ // |
+ // Since this is a private type with no methods, we need to use reflection |
+ // to get the data out. Ick. |
+ rv := reflect.ValueOf(val) |
+ if rv.Kind() == reflect.Struct && rv.Type().String() == "datastore.indexValue" { |
+ rv = rv.FieldByName("value") |
+ if rv.IsValid() && rv.Kind() == reflect.Ptr { |
+ // TODO(riannucci): determine if this is how nil IndexValues are stored. |
+ // Maybe they're encoded as a PropertyValue with all-nil fields instead? |
+ if rv.IsNil() { |
+ return nil |
+ } |
+ rv = rv.Elem() |
+ // we're in protobuf land now. |
+ if rv.Type().Name() == "PropertyValue" { |
+ for i := 0; i < rv.NumField(); i++ { |
+ field := rv.Field(i) |
+ if field.Kind() == reflect.Ptr { |
+ if field.IsNil() { |
+ continue |
+ } |
+ field = field.Elem() |
+ switch field.Kind() { |
+ case reflect.Int64: |
+ return field.Int() |
+ case reflect.String: |
+ return field.String() |
+ case reflect.Bool: |
+ return field.Bool() |
+ case reflect.Float64: |
+ return field.Float() |
+ } |
+ switch field.Type().Name() { |
+ case "PropertyValue_PointValue": |
+ // Lat == X, Lng == Y b/c historical resons. |
+ return ds.GeoPoint{ |
+ Lat: field.FieldByName("X").Float(), |
+ Lng: field.FieldByName("Y").Float()} |
+ case "PropertyValue_ReferenceValue": |
+ aid := field.FieldByName("App").Elem().String() |
+ ns := "" |
+ if nsf := field.FieldByName("NameSpace"); !nsf.IsNil() { |
+ ns = nsf.Elem().String() |
+ } |
+ elems := field.FieldByName("Pathelement") |
+ toks := make([]ds.KeyTok, elems.Len()) |
+ for i := range toks { |
+ e := elems.Index(i).Elem() |
+ toks[i].Kind = e.FieldByName("Type").Elem().String() |
+ if iid := e.FieldByName("Id"); !iid.IsNil() { |
+ toks[i].IntID = iid.Elem().Int() |
+ } |
+ if sid := e.FieldByName("Name"); !sid.IsNil() { |
+ toks[i].StringID = sid.Elem().String() |
+ } |
+ } |
+ return dskey.NewToks(aid, ns, toks) |
+ } |
+ panic(fmt.Errorf( |
+ "UNKNOWN datastore.indexValue field type: %s", field.Type())) |
+ } |
+ // there's also the `XXX_unrecognized []byte` field, so don't panic |
+ // here. |
+ } |
+ panic(fmt.Errorf("cannot decode datastore.indexValue (no recognized field): %v", val)) |
+ } |
+ panic(fmt.Errorf("cannot decode datastore.indexValue (wrong inner type): %v", val)) |
+ } |
+ panic(fmt.Errorf("cannot decode datastore.indexValue: %v", val)) |
+ } else { |
+ return val |
+ } |
+} |
+ |
func (tf *typeFilter) Load(props []datastore.Property) error { |
tf.pm = make(ds.PropertyMap, len(props)) |
for _, p := range props { |
val := p.Value |
switch x := val.(type) { |
case datastore.ByteString: |
- val = ds.ByteString(x) |
+ val = []byte(x) |
case *datastore.Key: |
val = dsR2F(x) |
case appengine.BlobKey: |
@@ -35,6 +114,8 @@ func (tf *typeFilter) Load(props []datastore.Property) error { |
case time.Time: |
// "appengine" layer instantiates with Local timezone. |
val = x.UTC() |
+ default: |
+ val = maybeIndexValue(val) |
} |
prop := ds.Property{} |
is := ds.ShouldIndex |
@@ -62,17 +143,22 @@ func (tf *typeFilter) Save() ([]datastore.Property, error) { |
Multiple: multiple, |
NoIndex: prop.IndexSetting() == ds.NoIndex, |
} |
- switch x := prop.Value().(type) { |
- case ds.ByteString: |
- toAdd.Value = datastore.ByteString(x) |
- case ds.Key: |
- toAdd.Value = dsF2R(x) |
- case bs.Key: |
- toAdd.Value = appengine.BlobKey(x) |
- case ds.GeoPoint: |
- toAdd.Value = appengine.GeoPoint(x) |
+ switch prop.Type() { |
+ case ds.PTBytes: |
+ v := prop.Value().([]byte) |
+ if prop.IndexSetting() == ds.ShouldIndex { |
+ toAdd.Value = datastore.ByteString(v) |
+ } else { |
+ toAdd.Value = v |
+ } |
+ case ds.PTKey: |
+ toAdd.Value = dsF2R(prop.Value().(ds.Key)) |
+ case ds.PTBlobKey: |
+ toAdd.Value = appengine.BlobKey(prop.Value().(bs.Key)) |
+ case ds.PTGeoPoint: |
+ toAdd.Value = appengine.GeoPoint(prop.Value().(ds.GeoPoint)) |
default: |
- toAdd.Value = x |
+ toAdd.Value = prop.Value() |
} |
props = append(props, toAdd) |
} |