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

Unified Diff: service/datastore/multiarg.go

Issue 2007123002: datastore: Update AllocateIDs to take keys. (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/gae@master
Patch Set: Rebase, comments. Created 4 years, 6 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
« no previous file with comments | « service/datastore/key_test.go ('k') | service/datastore/raw_interface.go » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: service/datastore/multiarg.go
diff --git a/service/datastore/multiarg.go b/service/datastore/multiarg.go
index 009c3f178b6431b56d7bd8b910933ff27c9e6a63..13bc3f02e74d2e294af0081402378dc5671bb69b 100644
--- a/service/datastore/multiarg.go
+++ b/service/datastore/multiarg.go
@@ -11,6 +11,30 @@ import (
"github.com/luci/luci-go/common/errors"
)
+type metaMultiArgConstraints int
+
+const (
+ // mmaReadWrite allows a metaMultiArg to operate on any type that can be
+ // both read and written to.
+ mmaReadWrite metaMultiArgConstraints = iota
+ // mmaKeysOnly implies mmaReadWrite, with the further statement that the only
+ // operation that will be performed against the arguments will be key
+ // extraction.
+ mmaKeysOnly = iota
+ // mmaWriteKeys indicates that the caller is only going to write key
+ // values. This enables the same inputs as mmaReadWrite, but also allows
+ // []*Key.
+ mmaWriteKeys = iota
+)
+
+func (c metaMultiArgConstraints) allowSingleKey() bool {
+ return c == mmaKeysOnly
+}
+
+func (c metaMultiArgConstraints) keyOperationsOnly() bool {
+ return c >= mmaKeysOnly
+}
+
type multiArgType struct {
getMGS func(slot reflect.Value) MetaGetterSetter
getPLS func(slot reflect.Value) PropertyLoadSaver
@@ -33,8 +57,8 @@ func (mat *multiArgType) setPM(slot reflect.Value, pm PropertyMap) error {
return mat.getPLS(slot).Load(pm)
}
-func (mat *multiArgType) setKey(slot reflect.Value, k *Key) {
- PopulateKey(mat.getMGS(slot), k)
+func (mat *multiArgType) setKey(slot reflect.Value, k *Key) bool {
+ return populateKeyMGS(mat.getMGS(slot), k)
}
// parseArg checks that et is of type S, *S, I, P or *P, for some
@@ -44,13 +68,17 @@ func (mat *multiArgType) setKey(slot reflect.Value, k *Key) {
// If et is a chan type that implements PropertyLoadSaver, new elements will be
// allocated with a buffer of 0.
//
-// If keysOnly is true, a read-only key extraction multiArgType will be
-// returned if et is a *Key.
-func parseArg(et reflect.Type, keysOnly bool) *multiArgType {
+// If allowKey is true, et may additional be type *Key. Only MetaGetterSetter
+// fields will be populated in the result (see keyMGS).
+func parseArg(et reflect.Type, allowKeys bool) *multiArgType {
var mat multiArgType
- if keysOnly && et == typeOfKey {
- mat.getMGS = func(slot reflect.Value) MetaGetterSetter { return &keyExtractionMGS{key: slot.Interface().(*Key)} }
+ if et == typeOfKey {
+ if !allowKeys {
+ return nil
+ }
+
+ mat.getMGS = func(slot reflect.Value) MetaGetterSetter { return &keyMGS{slot: slot} }
return &mat
}
@@ -199,10 +227,10 @@ func mustParseMultiArg(et reflect.Type) *multiArgType {
if et.Kind() != reflect.Slice {
panic(fmt.Errorf("invalid argument type: expected slice, got %s", et))
}
- return mustParseArg(et.Elem())
+ return mustParseArg(et.Elem(), true)
}
-func mustParseArg(et reflect.Type) *multiArgType {
+func mustParseArg(et reflect.Type, sliceArg bool) *multiArgType {
if mat := parseArg(et, false); mat != nil {
return mat
}
@@ -230,13 +258,13 @@ func newKeyObjErr(aid, ns string, mgs MetaGetterSetter) (*Key, error) {
return NewKey(aid, ns, kind, sid, iid, par), nil
}
-func isOKSingleType(t reflect.Type, keysOnly bool) error {
+func isOKSingleType(t reflect.Type, allowKey bool) error {
switch {
case t == nil:
return errors.New("no type information")
case t.Implements(typeOfPropertyLoadSaver):
return nil
- case !keysOnly && t == typeOfKey:
+ case !allowKey && t == typeOfKey:
return errors.New("not user datatype")
case t.Kind() != reflect.Ptr:
@@ -249,19 +277,34 @@ func isOKSingleType(t reflect.Type, keysOnly bool) error {
}
}
-// keyExtractionMGS is a MetaGetterSetter that wraps a key and only implements
-// GetMeta, and even then only retrieves the wrapped key meta value, "key".
-type keyExtractionMGS struct {
- MetaGetterSetter
+// keyMGS is a MetaGetterSetter that wraps a single key value/slot. It only
+// implements operations on the "key" key.
+//
+// GetMeta will be implemented, returning the *Key for the "key" meta.
+//
+// If slot is addressable, SetMeta will allow it to be set to the supplied
+// Value.
+type keyMGS struct {
+ slot reflect.Value
+}
+
+func (mgs *keyMGS) GetAllMeta() PropertyMap {
+ return PropertyMap{"$key": []Property{MkPropertyNI(mgs.slot.Interface())}}
+}
- key *Key
+func (mgs *keyMGS) GetMeta(key string) (interface{}, bool) {
+ if key != "key" {
+ return nil, false
+ }
+ return mgs.slot.Interface(), true
}
-func (mgs *keyExtractionMGS) GetMeta(key string) (interface{}, bool) {
- if key == "key" {
- return mgs.key, true
+func (mgs *keyMGS) SetMeta(key string, value interface{}) bool {
+ if !(key == "key" && mgs.slot.CanAddr()) {
+ return false
}
- return nil, false
+ mgs.slot.Set(reflect.ValueOf(value))
+ return true
}
type metaMultiArgElement struct {
@@ -277,10 +320,18 @@ type metaMultiArg struct {
count int // total number of elements, flattening slices
}
-func makeMetaMultiArg(args []interface{}, keysOnly bool) (*metaMultiArg, error) {
+// makeMetaMultiArg returns a metaMultiArg for the supplied args.
+//
+// If an arg is a slice, a slice metaMultiArg will be returned, and errors for
+// that slice will be written into a positional MultiError if they occur.
+//
+// If keysOnly is true, the caller is instructing metaMultiArg to only extract
+// the datastore *Key from args. *Key entries will be permitted, but the caller
+// may not write to them (since keys are read-only structs).
+func makeMetaMultiArg(args []interface{}, c metaMultiArgConstraints) (*metaMultiArg, error) {
mma := metaMultiArg{
elems: make([]metaMultiArgElement, len(args)),
- keysOnly: keysOnly,
+ keysOnly: c.keyOperationsOnly(),
}
lme := errors.NewLazyMultiError(len(args))
@@ -297,21 +348,29 @@ func makeMetaMultiArg(args []interface{}, keysOnly bool) (*metaMultiArg, error)
// Try and treat the argument as a single-value first. This allows slices
// that implement PropertyLoadSaver to be properly treated as a single
// element.
- var err error
- isSlice := false
- mat := parseArg(vt, keysOnly)
- if mat == nil {
+ var (
+ mat *multiArgType
+ err error
+ isSlice = false
+ )
+ if mat = parseArg(vt, c.allowSingleKey()); mat == nil {
// If this is a slice, treat it as a slice of arg candidates.
+ //
+ // If only keys are being read/written, we allow a []*Key to be accepted
+ // here, since slices are addressable (write).
if v.Kind() == reflect.Slice {
isSlice = true
- mat = parseArg(vt.Elem(), keysOnly)
+ mat = parseArg(vt.Elem(), c.keyOperationsOnly())
}
} else {
// Single types need to be able to be assigned to.
- err = isOKSingleType(vt, keysOnly)
+ //
+ // We only allow *Key here when the keys cannot be written to, since *Key
+ // should not be modified in-place, as they are immutable.
+ err = isOKSingleType(vt, c.allowSingleKey())
}
- if mat == nil {
- err = errors.New("not a PLS or pointer-to-struct")
+ if mat == nil && err == nil {
+ err = errors.New("not a PLS, pointer-to-struct, or slice thereof")
}
if err != nil {
lme.Assign(i, fmt.Errorf("invalid input type (%T): %s", arg, err))
« no previous file with comments | « service/datastore/key_test.go ('k') | service/datastore/raw_interface.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698