| Index: service/datastore/interface.go
|
| diff --git a/service/datastore/interface.go b/service/datastore/interface.go
|
| index 718b9c15e0aa2060509a3018b27d71ae6c57a73a..d8dc3dbb6e55bf4d5c5c2ceeae538b47ef408b0d 100644
|
| --- a/service/datastore/interface.go
|
| +++ b/service/datastore/interface.go
|
| @@ -5,313 +5,690 @@
|
| package datastore
|
|
|
| import (
|
| + "fmt"
|
| + "reflect"
|
| +
|
| + "github.com/luci/luci-go/common/errors"
|
| "golang.org/x/net/context"
|
| )
|
|
|
| -// Interface is the 'user-friendly' interface to access the current filtered
|
| -// datastore service implementation.
|
| -//
|
| -// Note that in exchange for userfriendliness, this interface ends up doing
|
| -// a lot of reflection.
|
| -//
|
| -// Methods taking 'interface{}' objects describe what a valid type for that
|
| -// interface are in the comments.
|
| -//
|
| -// Struct objects passed in will be converted to PropertyLoadSaver interfaces
|
| -// using this package's GetPLS function.
|
| -type Interface interface {
|
| - // AllocateIDs allows you to allocate IDs from the datastore without putting
|
| - // any data.
|
| - //
|
| - // A partial valid key will be constructed from each entity's kind and parent,
|
| - // if present. An allocation will then be performed against the datastore for
|
| - // each key, and the partial key will be populated with a unique integer ID.
|
| - // The resulting keys will be applied to their objects using PopulateKey. If
|
| - // successful, any existing ID will be destroyed.
|
| - //
|
| - // If the object is supplied that cannot accept an integer key, this method
|
| - // will panic.
|
| - //
|
| - // ent must be one of:
|
| - // - *S where S is a struct
|
| - // - *P where *P is a concrete type implementing PropertyLoadSaver
|
| - // - []S or []*S where S is a struct
|
| - // - []P or []*P where *P is a concrete type implementing PropertyLoadSaver
|
| - // - []I where i is some interface type. Each element of the slice must
|
| - // be non-nil, and its underlying type must be either *S or *P.
|
| - // - []*Key, to populate a slice of partial-valid keys.
|
| - //
|
| - // If an error is encountered, the returned error value will depend on the
|
| - // input arguments. If one argument is supplied, the result will be the
|
| - // encountered error type. If multiple arguments are supplied, the result will
|
| - // be a MultiError whose error index corresponds to the argument in which the
|
| - // error was encountered.
|
| - //
|
| - // If an ent argument is a slice, its error type will be a MultiError. Note
|
| - // that in the scenario where multiple slices are provided, this will return a
|
| - // MultiError containing a nested MultiError for each slice argument.
|
| - AllocateIDs(ent ...interface{}) error
|
| -
|
| - // KeyForObj extracts a key from src.
|
| - //
|
| - // It is the same as KeyForObjErr, except that if KeyForObjErr would have
|
| - // returned an error, this method panics. It's safe to use if you know that
|
| - // src statically meets the metadata constraints described by KeyForObjErr.
|
| - KeyForObj(src interface{}) *Key
|
| -
|
| - // MakeKey is a convenience method for manufacturing a *Key. It should only be
|
| - // used when elems... is known statically (e.g. in the code) to be correct.
|
| - //
|
| - // elems is pairs of (string, string|int|int32|int64) pairs, which correspond
|
| - // to Kind/id pairs. Example:
|
| - // dstore.MakeKey("Parent", 1, "Child", "id")
|
| - //
|
| - // Would create the key:
|
| - // <current appID>:<current Namespace>:/Parent,1/Child,id
|
| - //
|
| - // If elems is not parsable (e.g. wrong length, wrong types, etc.) this method
|
| - // will panic.
|
| - MakeKey(elems ...interface{}) *Key
|
| -
|
| - // NewKey constructs a new key in the current appID/Namespace, using the
|
| - // specified parameters.
|
| - NewKey(kind, stringID string, intID int64, parent *Key) *Key
|
| -
|
| - // NewIncompleteKeys allocates count incomplete keys sharing the same kind and
|
| - // parent. It is useful as input to AllocateIDs.
|
| - NewIncompleteKeys(count int, kind string, parent *Key) []*Key
|
| -
|
| - // NewKeyToks constructs a new key in the current appID/Namespace, using the
|
| - // specified key tokens.
|
| - NewKeyToks([]KeyTok) *Key
|
| -
|
| - // KeyForObjErr extracts a key from src.
|
| - //
|
| - // src must be one of:
|
| - // - *S, where S is a struct
|
| - // - a PropertyLoadSaver
|
| - //
|
| - // It is expected that the struct exposes the following metadata (as retrieved
|
| - // by MetaGetter.GetMeta):
|
| - // - "key" (type: Key) - The full datastore key to use. Must not be nil.
|
| - // OR
|
| - // - "id" (type: int64 or string) - The id of the Key to create
|
| - // - "kind" (optional, type: string) - The kind of the Key to create. If
|
| - // blank or not present, KeyForObjErr will extract the name of the src
|
| - // object's type.
|
| - // - "parent" (optional, type: Key) - The parent key to use.
|
| - //
|
| - // By default, the metadata will be extracted from the struct and its tagged
|
| - // properties. However, if the struct implements MetaGetterSetter it is
|
| - // wholly responsible for exporting the required fields. A struct that
|
| - // implements GetMeta to make some minor tweaks can evoke the defualt behavior
|
| - // by using GetPLS(s).GetMeta.
|
| - //
|
| - // If a required metadata item is missing or of the wrong type, then this will
|
| - // return an error.
|
| - KeyForObjErr(src interface{}) (*Key, error)
|
| -
|
| - // RunInTransaction runs f inside of a transaction. See the appengine SDK's
|
| - // documentation for full details on the behavior of transactions in the
|
| - // datastore.
|
| - //
|
| - // Note that the behavior of transactions may change depending on what filters
|
| - // have been installed. It's possible that we'll end up implementing things
|
| - // like nested/buffered transactions as filters.
|
| - RunInTransaction(f func(c context.Context) error, opts *TransactionOptions) error
|
| -
|
| - // Run executes the given query, and calls `cb` for each successfully
|
| - // retrieved item.
|
| - //
|
| - // cb is a callback function whose signature is
|
| - // func(obj TYPE[, getCursor CursorCB]) [error]
|
| - //
|
| - // Where TYPE is one of:
|
| - // - S or *S, where S is a struct
|
| - // - P or *P, where *P is a concrete type implementing PropertyLoadSaver
|
| - // - *Key (implies a keys-only query)
|
| - //
|
| - // If the error is omitted from the signature, this will run until the query
|
| - // returns all its results, or has an error/times out.
|
| - //
|
| - // If error is in the signature, the query will continue as long as the
|
| - // callback returns nil. If it returns `Stop`, the query will stop and Run
|
| - // will return nil. Otherwise, the query will stop and Run will return the
|
| - // user's error.
|
| - //
|
| - // Run may also stop on the first datastore error encountered, which can occur
|
| - // due to flakiness, timeout, etc. If it encounters such an error, it will
|
| - // be returned.
|
| - Run(q *Query, cb interface{}) error
|
| -
|
| - // Count executes the given query and returns the number of entries which
|
| - // match it.
|
| - Count(q *Query) (int64, error)
|
| -
|
| - // DecodeCursor converts a string returned by a Cursor into a Cursor instance.
|
| - // It will return an error if the supplied string is not valid, or could not
|
| - // be decoded by the implementation.
|
| - DecodeCursor(string) (Cursor, error)
|
| -
|
| - // GetAll retrieves all of the Query results into dst.
|
| - //
|
| - // dst must be one of:
|
| - // - *[]S or *[]*S, where S is a struct
|
| - // - *[]P or *[]*P, where *P is a concrete type implementing
|
| - // PropertyLoadSaver
|
| - // - *[]*Key implies a keys-only query.
|
| - GetAll(q *Query, dst interface{}) error
|
| -
|
| - // Exists tests if the supplied objects are present in the datastore.
|
| - //
|
| - // ent must be one of:
|
| - // - *S, where S is a struct
|
| - // - *P, where *P is a concrete type implementing PropertyLoadSaver
|
| - // - []S or []*S, where S is a struct
|
| - // - []P or []*P, where *P is a concrete type implementing PropertyLoadSaver
|
| - // - []I, where I is some interface type. Each element of the slice must
|
| - // be non-nil, and its underlying type must be either *S or *P.
|
| - // - *Key, to check a specific key from the datastore.
|
| - // - []*Key, to check a slice of keys from the datastore.
|
| - //
|
| - // If an error is encountered, the returned error value will depend on the
|
| - // input arguments. If one argument is supplied, the result will be the
|
| - // encountered error type. If multiple arguments are supplied, the result will
|
| - // be a MultiError whose error index corresponds to the argument in which the
|
| - // error was encountered.
|
| - //
|
| - // If an ent argument is a slice, its error type will be a MultiError. Note
|
| - // that in the scenario, where multiple slices are provided, this will return a
|
| - // MultiError containing a nested MultiError for each slice argument.
|
| - Exists(ent ...interface{}) (*ExistsResult, error)
|
| -
|
| - // Does a GetMulti for thes keys and returns true iff they exist. Will only
|
| - // return an error if it's not ErrNoSuchEntity. This is slightly more
|
| - // efficient than using Get directly, because it uses the underlying
|
| - // RawInterface to avoid some reflection and copies.
|
| - //
|
| - // If an error is encountered, the returned error will be a MultiError whose
|
| - // error index corresponds to the key for which the error was encountered.
|
| - //
|
| - // NOTE: ExistsMulti is obsolete. The vararg-accepting Exists should be used
|
| - // instead. This is left for backwards compatibility, but will be removed from
|
| - // this interface at some point in the future.
|
| - ExistsMulti(k []*Key) (BoolList, error)
|
| -
|
| - // Get retrieves objects from the datastore.
|
| - //
|
| - // Each element in dst must be one of:
|
| - // - *S, where S is a struct
|
| - // - *P, where *P is a concrete type implementing PropertyLoadSaver
|
| - // - []S or []*S, where S is a struct
|
| - // - []P or []*P, where *P is a concrete type implementing PropertyLoadSaver
|
| - // - []I, where I is some interface type. Each element of the slice must
|
| - // be non-nil, and its underlying type must be either *S or *P.
|
| - //
|
| - // If an error is encountered, the returned error value will depend on the
|
| - // input arguments. If one argument is supplied, the result will be the
|
| - // encountered error type. If multiple arguments are supplied, the result will
|
| - // be a MultiError whose error index corresponds to the argument in which the
|
| - // error was encountered.
|
| - //
|
| - // If a dst argument is a slice, its error type will be a MultiError. Note
|
| - // that in the scenario where multiple slices are provided, this will return a
|
| - // MultiError containing a nested MultiError for each slice argument.
|
| - Get(dst ...interface{}) error
|
| -
|
| - // GetMulti retrieves items from the datastore.
|
| - //
|
| - // dst must be one of:
|
| - // - []S or []*S, where S is a struct
|
| - // - []P or []*P, where *P is a concrete type implementing PropertyLoadSaver
|
| - // - []I, where I is some interface type. Each element of the slice must
|
| - // be non-nil, and its underlying type must be either *S or *P.
|
| - //
|
| - // NOTE: GetMulti is obsolete. The vararg-accepting Get should be used
|
| - // instead. This is left for backwards compatibility, but will be removed from
|
| - // this interface at some point in the future.
|
| - GetMulti(dst interface{}) error
|
| -
|
| - // Put writes objects into the datastore.
|
| - //
|
| - // src must be one of:
|
| - // - *S, where S is a struct
|
| - // - *P, where *P is a concrete type implementing PropertyLoadSaver
|
| - // - []S or []*S, where S is a struct
|
| - // - []P or []*P, where *P is a concrete type implementing PropertyLoadSaver
|
| - // - []I, where I is some interface type. Each element of the slice must
|
| - // be non-nil, and its underlying type must be either *S or *P.
|
| - //
|
| - // A *Key will be extracted from src via KeyForObj. If
|
| - // extractedKey.Incomplete() is true, then Put will write the resolved (i.e.
|
| - // automatic datastore-populated) *Key back to src.
|
| - //
|
| - // If an error is encountered, the returned error value will depend on the
|
| - // input arguments. If one argument is supplied, the result will be the
|
| - // encountered error type. If multiple arguments are supplied, the result will
|
| - // be a MultiError whose error index corresponds to the argument in which the
|
| - // error was encountered.
|
| - //
|
| - // If a src argument is a slice, its error type will be a MultiError. Note
|
| - // that in the scenario where multiple slices are provided, this will return a
|
| - // MultiError containing a nested MultiError for each slice argument.
|
| - Put(src ...interface{}) error
|
| -
|
| - // PutMulti writes items to the datastore.
|
| - //
|
| - // src must be one of:
|
| - // - []S or []*S, where S is a struct
|
| - // - []P or []*P, where *P is a concrete type implementing PropertyLoadSaver
|
| - // - []I, where I is some interface type. Each element of the slice must
|
| - // be non-nil, and its underlying type must be either *S or *P.
|
| - //
|
| - // If items in src resolve to Incomplete keys, PutMulti will write the
|
| - // resolved keys back to the items in src.
|
| - //
|
| - // NOTE: PutMulti is obsolete. The vararg-accepting Put should be used
|
| - // instead. This is left for backwards compatibility, but will be removed from
|
| - // this interface at some point in the future.
|
| - PutMulti(src interface{}) error
|
| -
|
| - // Delete removes the supplied entities from the datastore.
|
| - //
|
| - // ent must be one of:
|
| - // - *S, where S is a struct
|
| - // - *P, where *P is a concrete type implementing PropertyLoadSaver
|
| - // - []S or []*S, where S is a struct
|
| - // - []P or []*P, where *P is a concrete type implementing PropertyLoadSaver
|
| - // - []I, where I is some interface type. Each element of the slice must
|
| - // be non-nil, and its underlying type must be either *S or *P.
|
| - // - *Key, to remove a specific key from the datastore.
|
| - // - []*Key, to remove a slice of keys from the datastore.
|
| - //
|
| - // If an error is encountered, the returned error value will depend on the
|
| - // input arguments. If one argument is supplied, the result will be the
|
| - // encountered error type. If multiple arguments are supplied, the result will
|
| - // be a MultiError whose error index corresponds to the argument in which the
|
| - // error was encountered.
|
| - //
|
| - // If an ent argument is a slice, its error type will be a MultiError. Note
|
| - // that in the scenario where multiple slices are provided, this will return a
|
| - // MultiError containing a nested MultiError for each slice argument.
|
| - Delete(ent ...interface{}) error
|
| -
|
| - // DeleteMulti removes keys from the datastore.
|
| - //
|
| - // If an error is encountered, the returned error will be a MultiError whose
|
| - // error index corresponds to the key for which the error was encountered.
|
| - //
|
| - // NOTE: DeleteMulti is obsolete. The vararg-accepting Delete should be used
|
| - // instead. This is left for backwards compatibility, but will be removed from
|
| - // this interface at some point in the future.
|
| - DeleteMulti(keys []*Key) error
|
| -
|
| - // Testable returns the Testable interface for the implementation, or nil if
|
| - // there is none.
|
| - Testable() Testable
|
| -
|
| - // Raw returns the underlying RawInterface. The Interface and RawInterface may
|
| - // be used interchangably; there's no danger of interleaving access to the
|
| - // datastore via the two.
|
| - Raw() RawInterface
|
| +func runParseCallback(cbIface interface{}) (isKey, hasErr, hasCursorCB bool, mat *multiArgType) {
|
| + badSig := func() {
|
| + panic(fmt.Errorf(
|
| + "cb does not match the required callback signature: `%T` != `func(TYPE, [CursorCB]) [error]`",
|
| + cbIface))
|
| + }
|
| +
|
| + if cbIface == nil {
|
| + badSig()
|
| + }
|
| +
|
| + // TODO(riannucci): Profile and determine if any of this is causing a real
|
| + // slowdown. Could potentially cache reflection stuff by cbTyp?
|
| + cbTyp := reflect.TypeOf(cbIface)
|
| +
|
| + if cbTyp.Kind() != reflect.Func {
|
| + badSig()
|
| + }
|
| +
|
| + numIn := cbTyp.NumIn()
|
| + if numIn != 1 && numIn != 2 {
|
| + badSig()
|
| + }
|
| +
|
| + firstArg := cbTyp.In(0)
|
| + if firstArg == typeOfKey {
|
| + isKey = true
|
| + } else {
|
| + mat = mustParseArg(firstArg, false)
|
| + if mat.newElem == nil {
|
| + badSig()
|
| + }
|
| + }
|
| +
|
| + hasCursorCB = numIn == 2
|
| + if hasCursorCB && cbTyp.In(1) != typeOfCursorCB {
|
| + badSig()
|
| + }
|
| +
|
| + if cbTyp.NumOut() > 1 {
|
| + badSig()
|
| + } else if cbTyp.NumOut() == 1 && cbTyp.Out(0) != typeOfError {
|
| + badSig()
|
| + }
|
| + hasErr = cbTyp.NumOut() == 1
|
| +
|
| + return
|
| +}
|
| +
|
| +// AllocateIDs allows you to allocate IDs from the datastore without putting
|
| +// any data.
|
| +//
|
| +// A partial valid key will be constructed from each entity's kind and parent,
|
| +// if present. An allocation will then be performed against the datastore for
|
| +// each key, and the partial key will be populated with a unique integer ID.
|
| +// The resulting keys will be applied to their objects using PopulateKey. If
|
| +// successful, any existing ID will be destroyed.
|
| +//
|
| +// If the object is supplied that cannot accept an integer key, this method
|
| +// will panic.
|
| +//
|
| +// ent must be one of:
|
| +// - *S where S is a struct
|
| +// - *P where *P is a concrete type implementing PropertyLoadSaver
|
| +// - []S or []*S where S is a struct
|
| +// - []P or []*P where *P is a concrete type implementing PropertyLoadSaver
|
| +// - []I where i is some interface type. Each element of the slice must
|
| +// be non-nil, and its underlying type must be either *S or *P.
|
| +// - []*Key, to populate a slice of partial-valid keys.
|
| +//
|
| +// If an error is encountered, the returned error value will depend on the
|
| +// input arguments. If one argument is supplied, the result will be the
|
| +// encountered error type. If multiple arguments are supplied, the result will
|
| +// be a MultiError whose error index corresponds to the argument in which the
|
| +// error was encountered.
|
| +//
|
| +// If an ent argument is a slice, its error type will be a MultiError. Note
|
| +// that in the scenario where multiple slices are provided, this will return a
|
| +// MultiError containing a nested MultiError for each slice argument.
|
| +func AllocateIDs(c context.Context, ent ...interface{}) error {
|
| + if len(ent) == 0 {
|
| + return nil
|
| + }
|
| +
|
| + mma, err := makeMetaMultiArg(ent, mmaWriteKeys)
|
| + if err != nil {
|
| + panic(err)
|
| + }
|
| +
|
| + keys, _, err := mma.getKeysPMs(GetKeyContext(c), false)
|
| + if err != nil {
|
| + return maybeSingleError(err, ent)
|
| + }
|
| + if len(keys) == 0 {
|
| + return nil
|
| + }
|
| +
|
| + // Convert each key to be partial valid, assigning an integer ID of 0. Confirm
|
| + // that each object can be populated with such a key.
|
| + for i, key := range keys {
|
| + keys[i] = key.Incomplete()
|
| + }
|
| +
|
| + var et errorTracker
|
| + it := mma.iterator(et.init(mma))
|
| + err = filterStop(Raw(c).AllocateIDs(keys, func(key *Key, err error) error {
|
| + it.next(func(mat *multiArgType, v reflect.Value) error {
|
| + if err != nil {
|
| + return err
|
| + }
|
| +
|
| + if !mat.setKey(v, key) {
|
| + return ErrInvalidKey
|
| + }
|
| + return nil
|
| + })
|
| +
|
| + return nil
|
| + }))
|
| + if err == nil {
|
| + err = et.error()
|
| + }
|
| + return maybeSingleError(err, ent)
|
| +}
|
| +
|
| +// KeyForObj extracts a key from src.
|
| +//
|
| +// It is the same as KeyForObjErr, except that if KeyForObjErr would have
|
| +// returned an error, this method panics. It's safe to use if you know that
|
| +// src statically meets the metadata constraints described by KeyForObjErr.
|
| +func KeyForObj(c context.Context, src interface{}) *Key {
|
| + ret, err := KeyForObjErr(c, src)
|
| + if err != nil {
|
| + panic(err)
|
| + }
|
| + return ret
|
| +}
|
| +
|
| +// KeyForObjErr extracts a key from src.
|
| +//
|
| +// src must be one of:
|
| +// - *S, where S is a struct
|
| +// - a PropertyLoadSaver
|
| +//
|
| +// It is expected that the struct exposes the following metadata (as retrieved
|
| +// by MetaGetter.GetMeta):
|
| +// - "key" (type: Key) - The full datastore key to use. Must not be nil.
|
| +// OR
|
| +// - "id" (type: int64 or string) - The id of the Key to create
|
| +// - "kind" (optional, type: string) - The kind of the Key to create. If
|
| +// blank or not present, KeyForObjErr will extract the name of the src
|
| +// object's type.
|
| +// - "parent" (optional, type: Key) - The parent key to use.
|
| +//
|
| +// By default, the metadata will be extracted from the struct and its tagged
|
| +// properties. However, if the struct implements MetaGetterSetter it is
|
| +// wholly responsible for exporting the required fields. A struct that
|
| +// implements GetMeta to make some minor tweaks can evoke the defualt behavior
|
| +// by using GetPLS(s).GetMeta.
|
| +//
|
| +// If a required metadata item is missing or of the wrong type, then this will
|
| +// return an error.
|
| +func KeyForObjErr(c context.Context, src interface{}) (*Key, error) {
|
| + return newKeyObjErr(GetKeyContext(c), getMGS(src))
|
| +}
|
| +
|
| +// MakeKey is a convenience method for manufacturing a *Key. It should only be
|
| +// used when elems... is known statically (e.g. in the code) to be correct.
|
| +//
|
| +// elems is pairs of (string, string|int|int32|int64) pairs, which correspond
|
| +// to Kind/id pairs. Example:
|
| +// dstore.MakeKey("Parent", 1, "Child", "id")
|
| +//
|
| +// Would create the key:
|
| +// <current appID>:<current Namespace>:/Parent,1/Child,id
|
| +//
|
| +// If elems is not parsable (e.g. wrong length, wrong types, etc.) this method
|
| +// will panic.
|
| +func MakeKey(c context.Context, elems ...interface{}) *Key {
|
| + kc := GetKeyContext(c)
|
| + return kc.MakeKey(elems...)
|
| +}
|
| +
|
| +// NewKey constructs a new key in the current appID/Namespace, using the
|
| +// specified parameters.
|
| +func NewKey(c context.Context, kind, stringID string, intID int64, parent *Key) *Key {
|
| + kc := GetKeyContext(c)
|
| + return kc.NewKey(kind, stringID, intID, parent)
|
| +}
|
| +
|
| +// NewIncompleteKeys allocates count incomplete keys sharing the same kind and
|
| +// parent. It is useful as input to AllocateIDs.
|
| +func NewIncompleteKeys(c context.Context, count int, kind string, parent *Key) (keys []*Key) {
|
| + kc := GetKeyContext(c)
|
| + if count > 0 {
|
| + keys = make([]*Key, count)
|
| + for i := range keys {
|
| + keys[i] = kc.NewKey(kind, "", 0, parent)
|
| + }
|
| + }
|
| + return
|
| +}
|
| +
|
| +// NewKeyToks constructs a new key in the current appID/Namespace, using the
|
| +// specified key tokens.
|
| +func NewKeyToks(c context.Context, toks []KeyTok) *Key {
|
| + kc := GetKeyContext(c)
|
| + return kc.NewKeyToks(toks)
|
| +}
|
| +
|
| +// PopulateKey loads key into obj.
|
| +//
|
| +// obj is any object that Interface.Get is able to accept.
|
| +//
|
| +// Upon successful application, this method will return true. If the key could
|
| +// not be applied to the object, this method will return false. It will panic if
|
| +// obj is an invalid datastore model.
|
| +//
|
| +// This method will panic if obj is an invalid datastore model. If the key could
|
| +// not be applied to the object, nothing will happen.
|
| +func PopulateKey(obj interface{}, key *Key) bool {
|
| + return populateKeyMGS(getMGS(obj), key)
|
| +}
|
| +
|
| +func populateKeyMGS(mgs MetaGetterSetter, key *Key) bool {
|
| + if mgs.SetMeta("key", key) {
|
| + return true
|
| + }
|
| +
|
| + lst := key.LastTok()
|
| + if lst.StringID != "" {
|
| + if !mgs.SetMeta("id", lst.StringID) {
|
| + return false
|
| + }
|
| + } else {
|
| + if !mgs.SetMeta("id", lst.IntID) {
|
| + return false
|
| + }
|
| + }
|
| +
|
| + mgs.SetMeta("kind", lst.Kind)
|
| + mgs.SetMeta("parent", key.Parent())
|
| + return true
|
| +}
|
| +
|
| +// RunInTransaction runs f inside of a transaction. See the appengine SDK's
|
| +// documentation for full details on the behavior of transactions in the
|
| +// datastore.
|
| +//
|
| +// Note that the behavior of transactions may change depending on what filters
|
| +// have been installed. It's possible that we'll end up implementing things
|
| +// like nested/buffered transactions as filters.
|
| +func RunInTransaction(c context.Context, f func(c context.Context) error, opts *TransactionOptions) error {
|
| + return Raw(c).RunInTransaction(f, opts)
|
| +}
|
| +
|
| +// Run executes the given query, and calls `cb` for each successfully
|
| +// retrieved item.
|
| +//
|
| +// cb is a callback function whose signature is
|
| +// func(obj TYPE[, getCursor CursorCB]) [error]
|
| +//
|
| +// Where TYPE is one of:
|
| +// - S or *S, where S is a struct
|
| +// - P or *P, where *P is a concrete type implementing PropertyLoadSaver
|
| +// - *Key (implies a keys-only query)
|
| +//
|
| +// If the error is omitted from the signature, this will run until the query
|
| +// returns all its results, or has an error/times out.
|
| +//
|
| +// If error is in the signature, the query will continue as long as the
|
| +// callback returns nil. If it returns `Stop`, the query will stop and Run
|
| +// will return nil. Otherwise, the query will stop and Run will return the
|
| +// user's error.
|
| +//
|
| +// Run may also stop on the first datastore error encountered, which can occur
|
| +// due to flakiness, timeout, etc. If it encounters such an error, it will
|
| +// be returned.
|
| +func Run(c context.Context, q *Query, cb interface{}) error {
|
| + isKey, hasErr, hasCursorCB, mat := runParseCallback(cb)
|
| +
|
| + if isKey {
|
| + q = q.KeysOnly(true)
|
| + }
|
| + fq, err := q.Finalize()
|
| + if err != nil {
|
| + return err
|
| + }
|
| +
|
| + cbVal := reflect.ValueOf(cb)
|
| + var cbFunc func(reflect.Value, CursorCB) error
|
| + switch {
|
| + case hasErr && hasCursorCB:
|
| + cbFunc = func(v reflect.Value, cb CursorCB) error {
|
| + err := cbVal.Call([]reflect.Value{v, reflect.ValueOf(cb)})[0].Interface()
|
| + if err != nil {
|
| + return err.(error)
|
| + }
|
| + return nil
|
| + }
|
| +
|
| + case hasErr && !hasCursorCB:
|
| + cbFunc = func(v reflect.Value, _ CursorCB) error {
|
| + err := cbVal.Call([]reflect.Value{v})[0].Interface()
|
| + if err != nil {
|
| + return err.(error)
|
| + }
|
| + return nil
|
| + }
|
| +
|
| + case !hasErr && hasCursorCB:
|
| + cbFunc = func(v reflect.Value, cb CursorCB) error {
|
| + cbVal.Call([]reflect.Value{v, reflect.ValueOf(cb)})
|
| + return nil
|
| + }
|
| +
|
| + case !hasErr && !hasCursorCB:
|
| + cbFunc = func(v reflect.Value, _ CursorCB) error {
|
| + cbVal.Call([]reflect.Value{v})
|
| + return nil
|
| + }
|
| + }
|
| +
|
| + raw := Raw(c)
|
| + if isKey {
|
| + err = raw.Run(fq, func(k *Key, _ PropertyMap, gc CursorCB) error {
|
| + return cbFunc(reflect.ValueOf(k), gc)
|
| + })
|
| + } else {
|
| + err = raw.Run(fq, func(k *Key, pm PropertyMap, gc CursorCB) error {
|
| + itm := mat.newElem()
|
| + if err := mat.setPM(itm, pm); err != nil {
|
| + return err
|
| + }
|
| + mat.setKey(itm, k)
|
| + return cbFunc(itm, gc)
|
| + })
|
| + }
|
| + return filterStop(err)
|
| +}
|
| +
|
| +// Count executes the given query and returns the number of entries which
|
| +// match it.
|
| +func Count(c context.Context, q *Query) (int64, error) {
|
| + fq, err := q.Finalize()
|
| + if err != nil {
|
| + return 0, err
|
| + }
|
| + v, err := Raw(c).Count(fq)
|
| + return v, filterStop(err)
|
| +}
|
| +
|
| +// DecodeCursor converts a string returned by a Cursor into a Cursor instance.
|
| +// It will return an error if the supplied string is not valid, or could not
|
| +// be decoded by the implementation.
|
| +func DecodeCursor(c context.Context, s string) (Cursor, error) {
|
| + return Raw(c).DecodeCursor(s)
|
| +}
|
| +
|
| +// GetAll retrieves all of the Query results into dst.
|
| +//
|
| +// dst must be one of:
|
| +// - *[]S or *[]*S, where S is a struct
|
| +// - *[]P or *[]*P, where *P is a concrete type implementing
|
| +// PropertyLoadSaver
|
| +// - *[]*Key implies a keys-only query.
|
| +func GetAll(c context.Context, q *Query, dst interface{}) error {
|
| + v := reflect.ValueOf(dst)
|
| + if v.Kind() != reflect.Ptr {
|
| + panic(fmt.Errorf("invalid GetAll dst: must have a ptr-to-slice: %T", dst))
|
| + }
|
| + if !v.IsValid() || v.IsNil() {
|
| + panic(errors.New("invalid GetAll dst: <nil>"))
|
| + }
|
| +
|
| + raw := Raw(c)
|
| + if keys, ok := dst.(*[]*Key); ok {
|
| + fq, err := q.KeysOnly(true).Finalize()
|
| + if err != nil {
|
| + return err
|
| + }
|
| +
|
| + return raw.Run(fq, func(k *Key, _ PropertyMap, _ CursorCB) error {
|
| + *keys = append(*keys, k)
|
| + return nil
|
| + })
|
| + }
|
| + fq, err := q.Finalize()
|
| + if err != nil {
|
| + return err
|
| + }
|
| +
|
| + slice := v.Elem()
|
| + mat := mustParseMultiArg(slice.Type())
|
| + if mat.newElem == nil {
|
| + panic(fmt.Errorf("invalid GetAll dst (non-concrete element type): %T", dst))
|
| + }
|
| +
|
| + errs := map[int]error{}
|
| + i := 0
|
| + err = filterStop(raw.Run(fq, func(k *Key, pm PropertyMap, _ CursorCB) error {
|
| + slice.Set(reflect.Append(slice, mat.newElem()))
|
| + itm := slice.Index(i)
|
| + mat.setKey(itm, k)
|
| + err := mat.setPM(itm, pm)
|
| + if err != nil {
|
| + errs[i] = err
|
| + }
|
| + i++
|
| + return nil
|
| + }))
|
| + if err == nil {
|
| + if len(errs) > 0 {
|
| + me := make(errors.MultiError, slice.Len())
|
| + for i, e := range errs {
|
| + me[i] = e
|
| + }
|
| + err = me
|
| + }
|
| + }
|
| + return err
|
| +}
|
| +
|
| +// Exists tests if the supplied objects are present in the datastore.
|
| +//
|
| +// ent must be one of:
|
| +// - *S, where S is a struct
|
| +// - *P, where *P is a concrete type implementing PropertyLoadSaver
|
| +// - []S or []*S, where S is a struct
|
| +// - []P or []*P, where *P is a concrete type implementing PropertyLoadSaver
|
| +// - []I, where I is some interface type. Each element of the slice must
|
| +// be non-nil, and its underlying type must be either *S or *P.
|
| +// - *Key, to check a specific key from the datastore.
|
| +// - []*Key, to check a slice of keys from the datastore.
|
| +//
|
| +// If an error is encountered, the returned error value will depend on the
|
| +// input arguments. If one argument is supplied, the result will be the
|
| +// encountered error type. If multiple arguments are supplied, the result will
|
| +// be a MultiError whose error index corresponds to the argument in which the
|
| +// error was encountered.
|
| +//
|
| +// If an ent argument is a slice, its error type will be a MultiError. Note
|
| +// that in the scenario, where multiple slices are provided, this will return a
|
| +// MultiError containing a nested MultiError for each slice argument.
|
| +func Exists(c context.Context, ent ...interface{}) (*ExistsResult, error) {
|
| + if len(ent) == 0 {
|
| + return nil, nil
|
| + }
|
| +
|
| + mma, err := makeMetaMultiArg(ent, mmaKeysOnly)
|
| + if err != nil {
|
| + panic(err)
|
| + }
|
| +
|
| + keys, _, err := mma.getKeysPMs(GetKeyContext(c), false)
|
| + if err != nil {
|
| + return nil, maybeSingleError(err, ent)
|
| + }
|
| + if len(keys) == 0 {
|
| + return nil, nil
|
| + }
|
| +
|
| + var bt boolTracker
|
| + it := mma.iterator(bt.init(mma))
|
| + err = filterStop(Raw(c).GetMulti(keys, nil, func(_ PropertyMap, err error) error {
|
| + it.next(func(*multiArgType, reflect.Value) error {
|
| + return err
|
| + })
|
| + return nil
|
| + }))
|
| + if err == nil {
|
| + err = bt.error()
|
| + }
|
| + return bt.result(), maybeSingleError(err, ent)
|
| +}
|
| +
|
| +// Get retrieves objects from the datastore.
|
| +//
|
| +// Each element in dst must be one of:
|
| +// - *S, where S is a struct
|
| +// - *P, where *P is a concrete type implementing PropertyLoadSaver
|
| +// - []S or []*S, where S is a struct
|
| +// - []P or []*P, where *P is a concrete type implementing PropertyLoadSaver
|
| +// - []I, where I is some interface type. Each element of the slice must
|
| +// be non-nil, and its underlying type must be either *S or *P.
|
| +//
|
| +// If an error is encountered, the returned error value will depend on the
|
| +// input arguments. If one argument is supplied, the result will be the
|
| +// encountered error type. If multiple arguments are supplied, the result will
|
| +// be a MultiError whose error index corresponds to the argument in which the
|
| +// error was encountered.
|
| +//
|
| +// If a dst argument is a slice, its error type will be a MultiError. Note
|
| +// that in the scenario where multiple slices are provided, this will return a
|
| +// MultiError containing a nested MultiError for each slice argument.
|
| +func Get(c context.Context, dst ...interface{}) error {
|
| + if len(dst) == 0 {
|
| + return nil
|
| + }
|
| +
|
| + mma, err := makeMetaMultiArg(dst, mmaReadWrite)
|
| + if err != nil {
|
| + panic(err)
|
| + }
|
| +
|
| + keys, pms, err := mma.getKeysPMs(GetKeyContext(c), true)
|
| + if err != nil {
|
| + return maybeSingleError(err, dst)
|
| + }
|
| + if len(keys) == 0 {
|
| + return nil
|
| + }
|
| +
|
| + var et errorTracker
|
| + it := mma.iterator(et.init(mma))
|
| + meta := NewMultiMetaGetter(pms)
|
| + err = filterStop(Raw(c).GetMulti(keys, meta, func(pm PropertyMap, err error) error {
|
| + it.next(func(mat *multiArgType, slot reflect.Value) error {
|
| + if err != nil {
|
| + return err
|
| + }
|
| + return mat.setPM(slot, pm)
|
| + })
|
| + return nil
|
| + }))
|
| +
|
| + if err == nil {
|
| + err = et.error()
|
| + }
|
| + return maybeSingleError(err, dst)
|
| +}
|
| +
|
| +// Put writes objects into the datastore.
|
| +//
|
| +// src must be one of:
|
| +// - *S, where S is a struct
|
| +// - *P, where *P is a concrete type implementing PropertyLoadSaver
|
| +// - []S or []*S, where S is a struct
|
| +// - []P or []*P, where *P is a concrete type implementing PropertyLoadSaver
|
| +// - []I, where I is some interface type. Each element of the slice must
|
| +// be non-nil, and its underlying type must be either *S or *P.
|
| +//
|
| +// A *Key will be extracted from src via KeyForObj. If
|
| +// extractedKey.Incomplete() is true, then Put will write the resolved (i.e.
|
| +// automatic datastore-populated) *Key back to src.
|
| +//
|
| +// If an error is encountered, the returned error value will depend on the
|
| +// input arguments. If one argument is supplied, the result will be the
|
| +// encountered error type. If multiple arguments are supplied, the result will
|
| +// be a MultiError whose error index corresponds to the argument in which the
|
| +// error was encountered.
|
| +//
|
| +// If a src argument is a slice, its error type will be a MultiError. Note
|
| +// that in the scenario where multiple slices are provided, this will return a
|
| +// MultiError containing a nested MultiError for each slice argument.
|
| +func Put(c context.Context, src ...interface{}) error {
|
| + if len(src) == 0 {
|
| + return nil
|
| + }
|
| +
|
| + mma, err := makeMetaMultiArg(src, mmaReadWrite)
|
| + if err != nil {
|
| + panic(err)
|
| + }
|
| +
|
| + keys, vals, err := mma.getKeysPMs(GetKeyContext(c), false)
|
| + if err != nil {
|
| + return maybeSingleError(err, src)
|
| + }
|
| + if len(keys) == 0 {
|
| + return nil
|
| + }
|
| +
|
| + i := 0
|
| + var et errorTracker
|
| + it := mma.iterator(et.init(mma))
|
| + err = filterStop(Raw(c).PutMulti(keys, vals, func(key *Key, err error) error {
|
| + it.next(func(mat *multiArgType, slot reflect.Value) error {
|
| + if err != nil {
|
| + return err
|
| + }
|
| + if key != keys[i] {
|
| + mat.setKey(slot, key)
|
| + }
|
| + return nil
|
| + })
|
| +
|
| + i++
|
| + return nil
|
| + }))
|
| +
|
| + if err == nil {
|
| + err = et.error()
|
| + }
|
| + return maybeSingleError(err, src)
|
| +}
|
| +
|
| +// Delete removes the supplied entities from the datastore.
|
| +//
|
| +// ent must be one of:
|
| +// - *S, where S is a struct
|
| +// - *P, where *P is a concrete type implementing PropertyLoadSaver
|
| +// - []S or []*S, where S is a struct
|
| +// - []P or []*P, where *P is a concrete type implementing PropertyLoadSaver
|
| +// - []I, where I is some interface type. Each element of the slice must
|
| +// be non-nil, and its underlying type must be either *S or *P.
|
| +// - *Key, to remove a specific key from the datastore.
|
| +// - []*Key, to remove a slice of keys from the datastore.
|
| +//
|
| +// If an error is encountered, the returned error value will depend on the
|
| +// input arguments. If one argument is supplied, the result will be the
|
| +// encountered error type. If multiple arguments are supplied, the result will
|
| +// be a MultiError whose error index corresponds to the argument in which the
|
| +// error was encountered.
|
| +//
|
| +// If an ent argument is a slice, its error type will be a MultiError. Note
|
| +// that in the scenario where multiple slices are provided, this will return a
|
| +// MultiError containing a nested MultiError for each slice argument.
|
| +func Delete(c context.Context, ent ...interface{}) error {
|
| + if len(ent) == 0 {
|
| + return nil
|
| + }
|
| +
|
| + mma, err := makeMetaMultiArg(ent, mmaKeysOnly)
|
| + if err != nil {
|
| + panic(err)
|
| + }
|
| +
|
| + keys, _, err := mma.getKeysPMs(GetKeyContext(c), false)
|
| + if err != nil {
|
| + return maybeSingleError(err, ent)
|
| + }
|
| + if len(keys) == 0 {
|
| + return nil
|
| + }
|
| +
|
| + var et errorTracker
|
| + it := mma.iterator(et.init(mma))
|
| + err = filterStop(Raw(c).DeleteMulti(keys, func(err error) error {
|
| + it.next(func(*multiArgType, reflect.Value) error {
|
| + return err
|
| + })
|
| +
|
| + return nil
|
| + }))
|
| + if err == nil {
|
| + err = et.error()
|
| + }
|
| + return maybeSingleError(err, ent)
|
| +}
|
| +
|
| +// GetTestable returns the Testable interface for the implementation, or nil if
|
| +// there is none.
|
| +func GetTestable(c context.Context) Testable {
|
| + return Raw(c).GetTestable()
|
| +}
|
| +
|
| +// maybeSingleError normalizes the error experience between single- and
|
| +// multi-element API calls.
|
| +//
|
| +// Single-element API calls will return a single error for that element, while
|
| +// multi-element API calls will return a MultiError, one for each element. This
|
| +// accepts the slice of elements that is being operated on and determines what
|
| +// sort of error to return.
|
| +func maybeSingleError(err error, elems []interface{}) error {
|
| + if err == nil {
|
| + return nil
|
| + }
|
| + if len(elems) == 1 {
|
| + return errors.SingleError(err)
|
| + }
|
| + return err
|
| +}
|
| +
|
| +func filterStop(err error) error {
|
| + if err == Stop {
|
| + err = nil
|
| + }
|
| + return err
|
| }
|
|
|