Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package datastore | 5 package datastore |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "fmt" | |
| 9 "reflect" | |
| 10 | |
| 11 "github.com/luci/luci-go/common/errors" | |
| 8 "golang.org/x/net/context" | 12 "golang.org/x/net/context" |
| 9 ) | 13 ) |
| 10 | 14 |
| 11 // Interface is the 'user-friendly' interface to access the current filtered | 15 func runParseCallback(cbIface interface{}) (isKey, hasErr, hasCursorCB bool, mat *multiArgType) { |
| 12 // datastore service implementation. | 16 badSig := func() { |
| 13 // | 17 panic(fmt.Errorf( |
| 14 // Note that in exchange for userfriendliness, this interface ends up doing | 18 "cb does not match the required callback signature: `%T` != `func(TYPE, [CursorCB]) [error]`", |
| 15 // a lot of reflection. | 19 cbIface)) |
| 16 // | 20 } |
| 17 // Methods taking 'interface{}' objects describe what a valid type for that | 21 |
| 18 // interface are in the comments. | 22 if cbIface == nil { |
| 19 // | 23 badSig() |
| 20 // Struct objects passed in will be converted to PropertyLoadSaver interfaces | 24 } |
| 21 // using this package's GetPLS function. | 25 |
| 22 type Interface interface { | 26 // TODO(riannucci): Profile and determine if any of this is causing a re al |
|
dnj
2016/09/01 15:25:40
Now gone. This is done for all service/* packages.
| |
| 23 // AllocateIDs allows you to allocate IDs from the datastore without put ting | 27 // slowdown. Could potentially cache reflection stuff by cbTyp? |
| 24 // any data. | 28 cbTyp := reflect.TypeOf(cbIface) |
| 25 // | 29 |
| 26 // A partial valid key will be constructed from each entity's kind and p arent, | 30 if cbTyp.Kind() != reflect.Func { |
| 27 // if present. An allocation will then be performed against the datastor e for | 31 badSig() |
| 28 // each key, and the partial key will be populated with a unique integer ID. | 32 } |
| 29 // The resulting keys will be applied to their objects using PopulateKey . If | 33 |
| 30 // successful, any existing ID will be destroyed. | 34 numIn := cbTyp.NumIn() |
| 31 // | 35 if numIn != 1 && numIn != 2 { |
| 32 // If the object is supplied that cannot accept an integer key, this met hod | 36 badSig() |
| 33 // will panic. | 37 } |
| 34 // | 38 |
| 35 // ent must be one of: | 39 firstArg := cbTyp.In(0) |
| 36 // - *S where S is a struct | 40 if firstArg == typeOfKey { |
| 37 // - *P where *P is a concrete type implementing PropertyLoadSaver | 41 isKey = true |
| 38 // - []S or []*S where S is a struct | 42 } else { |
| 39 // - []P or []*P where *P is a concrete type implementing PropertyL oadSaver | 43 mat = mustParseArg(firstArg, false) |
| 40 // - []I where i is some interface type. Each element of the slice must | 44 if mat.newElem == nil { |
| 41 // be non-nil, and its underlying type must be either *S or *P. | 45 badSig() |
| 42 // - []*Key, to populate a slice of partial-valid keys. | 46 } |
| 43 // | 47 } |
| 44 // If an error is encountered, the returned error value will depend on t he | 48 |
| 45 // input arguments. If one argument is supplied, the result will be the | 49 hasCursorCB = numIn == 2 |
| 46 // encountered error type. If multiple arguments are supplied, the resul t will | 50 if hasCursorCB && cbTyp.In(1) != typeOfCursorCB { |
| 47 // be a MultiError whose error index corresponds to the argument in whic h the | 51 badSig() |
| 48 // error was encountered. | 52 } |
| 49 // | 53 |
| 50 // If an ent argument is a slice, its error type will be a MultiError. N ote | 54 if cbTyp.NumOut() > 1 { |
| 51 // that in the scenario where multiple slices are provided, this will re turn a | 55 badSig() |
| 52 // MultiError containing a nested MultiError for each slice argument. | 56 } else if cbTyp.NumOut() == 1 && cbTyp.Out(0) != typeOfError { |
| 53 AllocateIDs(ent ...interface{}) error | 57 badSig() |
| 54 | 58 } |
| 55 // KeyForObj extracts a key from src. | 59 hasErr = cbTyp.NumOut() == 1 |
| 56 // | 60 |
| 57 // It is the same as KeyForObjErr, except that if KeyForObjErr would hav e | 61 return |
| 58 // returned an error, this method panics. It's safe to use if you know t hat | 62 } |
| 59 // src statically meets the metadata constraints described by KeyForObjE rr. | 63 |
| 60 KeyForObj(src interface{}) *Key | 64 // AllocateIDs allows you to allocate IDs from the datastore without putting |
| 61 | 65 // any data. |
| 62 // MakeKey is a convenience method for manufacturing a *Key. It should o nly be | 66 // |
| 63 // used when elems... is known statically (e.g. in the code) to be corre ct. | 67 // A partial valid key will be constructed from each entity's kind and parent, |
| 64 // | 68 // if present. An allocation will then be performed against the datastore for |
| 65 // elems is pairs of (string, string|int|int32|int64) pairs, which corre spond | 69 // each key, and the partial key will be populated with a unique integer ID. |
| 66 // to Kind/id pairs. Example: | 70 // The resulting keys will be applied to their objects using PopulateKey. If |
| 67 // dstore.MakeKey("Parent", 1, "Child", "id") | 71 // successful, any existing ID will be destroyed. |
| 68 // | 72 // |
| 69 // Would create the key: | 73 // If the object is supplied that cannot accept an integer key, this method |
| 70 // <current appID>:<current Namespace>:/Parent,1/Child,id | 74 // will panic. |
| 71 // | 75 // |
| 72 // If elems is not parsable (e.g. wrong length, wrong types, etc.) this method | 76 // ent must be one of: |
| 73 // will panic. | 77 // - *S where S is a struct |
| 74 MakeKey(elems ...interface{}) *Key | 78 // - *P where *P is a concrete type implementing PropertyLoadSaver |
| 75 | 79 // - []S or []*S where S is a struct |
| 76 // NewKey constructs a new key in the current appID/Namespace, using the | 80 // - []P or []*P where *P is a concrete type implementing PropertyLoadSaver |
| 77 // specified parameters. | 81 // - []I where i is some interface type. Each element of the slice must |
| 78 NewKey(kind, stringID string, intID int64, parent *Key) *Key | 82 // be non-nil, and its underlying type must be either *S or *P. |
| 79 | 83 // - []*Key, to populate a slice of partial-valid keys. |
| 80 // NewIncompleteKeys allocates count incomplete keys sharing the same ki nd and | 84 // |
| 81 // parent. It is useful as input to AllocateIDs. | 85 // If an error is encountered, the returned error value will depend on the |
| 82 NewIncompleteKeys(count int, kind string, parent *Key) []*Key | 86 // input arguments. If one argument is supplied, the result will be the |
| 83 | 87 // encountered error type. If multiple arguments are supplied, the result will |
| 84 // NewKeyToks constructs a new key in the current appID/Namespace, using the | 88 // be a MultiError whose error index corresponds to the argument in which the |
| 85 // specified key tokens. | 89 // error was encountered. |
| 86 NewKeyToks([]KeyTok) *Key | 90 // |
| 87 | 91 // If an ent argument is a slice, its error type will be a MultiError. Note |
| 88 // KeyForObjErr extracts a key from src. | 92 // that in the scenario where multiple slices are provided, this will return a |
| 89 // | 93 // MultiError containing a nested MultiError for each slice argument. |
| 90 // src must be one of: | 94 func AllocateIDs(c context.Context, ent ...interface{}) error { |
|
dnj
2016/09/01 15:25:40
Now top-level! This is basically just copy/paste w
| |
| 91 // - *S, where S is a struct | 95 if len(ent) == 0 { |
| 92 // - a PropertyLoadSaver | 96 return nil |
| 93 // | 97 } |
| 94 // It is expected that the struct exposes the following metadata (as ret rieved | 98 |
| 95 // by MetaGetter.GetMeta): | 99 mma, err := makeMetaMultiArg(ent, mmaWriteKeys) |
| 96 // - "key" (type: Key) - The full datastore key to use. Must not be ni l. | 100 if err != nil { |
| 97 // OR | 101 panic(err) |
| 98 // - "id" (type: int64 or string) - The id of the Key to create | 102 } |
| 99 // - "kind" (optional, type: string) - The kind of the Key to create. If | 103 |
| 100 // blank or not present, KeyForObjErr will extract the name of the s rc | 104 keys, _, err := mma.getKeysPMs(GetKeyContext(c), false) |
| 101 // object's type. | 105 if err != nil { |
| 102 // - "parent" (optional, type: Key) - The parent key to use. | 106 return err |
| 103 // | 107 } |
| 104 // By default, the metadata will be extracted from the struct and its ta gged | 108 if len(keys) == 0 { |
| 105 // properties. However, if the struct implements MetaGetterSetter it is | 109 return nil |
| 106 // wholly responsible for exporting the required fields. A struct that | 110 } |
| 107 // implements GetMeta to make some minor tweaks can evoke the defualt be havior | 111 |
| 108 // by using GetPLS(s).GetMeta. | 112 // Convert each key to be partial valid, assigning an integer ID of 0. C onfirm |
| 109 // | 113 // that each object can be populated with such a key. |
| 110 // If a required metadata item is missing or of the wrong type, then thi s will | 114 for i, key := range keys { |
| 111 // return an error. | 115 keys[i] = key.Incomplete() |
| 112 KeyForObjErr(src interface{}) (*Key, error) | 116 } |
| 113 | 117 |
| 114 // RunInTransaction runs f inside of a transaction. See the appengine SD K's | 118 var et errorTracker |
| 115 // documentation for full details on the behavior of transactions in the | 119 it := mma.iterator(et.init(mma)) |
| 116 // datastore. | 120 err = filterStop(Raw(c).AllocateIDs(keys, func(key *Key, err error) erro r { |
| 117 // | 121 it.next(func(mat *multiArgType, v reflect.Value) error { |
| 118 // Note that the behavior of transactions may change depending on what f ilters | 122 if err != nil { |
| 119 // have been installed. It's possible that we'll end up implementing thi ngs | 123 return err |
| 120 // like nested/buffered transactions as filters. | 124 } |
| 121 RunInTransaction(f func(c context.Context) error, opts *TransactionOptio ns) error | 125 |
| 122 | 126 if !mat.setKey(v, key) { |
| 123 // Run executes the given query, and calls `cb` for each successfully | 127 return ErrInvalidKey |
| 124 // retrieved item. | 128 } |
| 125 // | 129 return nil |
| 126 // cb is a callback function whose signature is | 130 }) |
| 127 // func(obj TYPE[, getCursor CursorCB]) [error] | 131 |
| 128 // | 132 return nil |
| 129 // Where TYPE is one of: | 133 })) |
| 130 // - S or *S, where S is a struct | 134 if err == nil { |
| 131 // - P or *P, where *P is a concrete type implementing PropertyLoadSav er | 135 err = et.error() |
| 132 // - *Key (implies a keys-only query) | 136 |
| 133 // | 137 if err != nil && len(ent) == 1 { |
| 134 // If the error is omitted from the signature, this will run until the q uery | 138 // Single-argument Exists will return a single error. |
| 135 // returns all its results, or has an error/times out. | 139 err = errors.SingleError(err) |
| 136 // | 140 } |
| 137 // If error is in the signature, the query will continue as long as the | 141 } |
| 138 // callback returns nil. If it returns `Stop`, the query will stop and R un | 142 return err |
| 139 // will return nil. Otherwise, the query will stop and Run will return t he | 143 } |
| 140 // user's error. | 144 |
| 141 // | 145 // KeyForObj extracts a key from src. |
| 142 // Run may also stop on the first datastore error encountered, which can occur | 146 // |
| 143 // due to flakiness, timeout, etc. If it encounters such an error, it wi ll | 147 // It is the same as KeyForObjErr, except that if KeyForObjErr would have |
| 144 // be returned. | 148 // returned an error, this method panics. It's safe to use if you know that |
| 145 Run(q *Query, cb interface{}) error | 149 // src statically meets the metadata constraints described by KeyForObjErr. |
| 146 | 150 func KeyForObj(c context.Context, src interface{}) *Key { |
| 147 // Count executes the given query and returns the number of entries whic h | 151 ret, err := KeyForObjErr(c, src) |
| 148 // match it. | 152 if err != nil { |
| 149 Count(q *Query) (int64, error) | 153 panic(err) |
| 150 | 154 } |
| 151 // DecodeCursor converts a string returned by a Cursor into a Cursor ins tance. | 155 return ret |
| 152 // It will return an error if the supplied string is not valid, or could not | 156 } |
| 153 // be decoded by the implementation. | 157 |
| 154 DecodeCursor(string) (Cursor, error) | 158 // KeyForObjErr extracts a key from src. |
| 155 | 159 // |
| 156 // GetAll retrieves all of the Query results into dst. | 160 // src must be one of: |
| 157 // | 161 // - *S, where S is a struct |
| 158 // dst must be one of: | 162 // - a PropertyLoadSaver |
| 159 // - *[]S or *[]*S, where S is a struct | 163 // |
| 160 // - *[]P or *[]*P, where *P is a concrete type implementing | 164 // It is expected that the struct exposes the following metadata (as retrieved |
| 161 // PropertyLoadSaver | 165 // by MetaGetter.GetMeta): |
| 162 // - *[]*Key implies a keys-only query. | 166 // - "key" (type: Key) - The full datastore key to use. Must not be nil. |
| 163 GetAll(q *Query, dst interface{}) error | 167 // OR |
| 164 | 168 // - "id" (type: int64 or string) - The id of the Key to create |
| 165 // Exists tests if the supplied objects are present in the datastore. | 169 // - "kind" (optional, type: string) - The kind of the Key to create. If |
| 166 // | 170 // blank or not present, KeyForObjErr will extract the name of the src |
| 167 // ent must be one of: | 171 // object's type. |
| 168 // - *S, where S is a struct | 172 // - "parent" (optional, type: Key) - The parent key to use. |
| 169 // - *P, where *P is a concrete type implementing PropertyLoadSaver | 173 // |
| 170 // - []S or []*S, where S is a struct | 174 // By default, the metadata will be extracted from the struct and its tagged |
| 171 // - []P or []*P, where *P is a concrete type implementing Property LoadSaver | 175 // properties. However, if the struct implements MetaGetterSetter it is |
| 172 // - []I, where I is some interface type. Each element of the slice must | 176 // wholly responsible for exporting the required fields. A struct that |
| 173 // be non-nil, and its underlying type must be either *S or *P. | 177 // implements GetMeta to make some minor tweaks can evoke the defualt behavior |
| 174 // - *Key, to check a specific key from the datastore. | 178 // by using GetPLS(s).GetMeta. |
| 175 // - []*Key, to check a slice of keys from the datastore. | 179 // |
| 176 // | 180 // If a required metadata item is missing or of the wrong type, then this will |
| 177 // If an error is encountered, the returned error value will depend on t he | 181 // return an error. |
| 178 // input arguments. If one argument is supplied, the result will be the | 182 func KeyForObjErr(c context.Context, src interface{}) (*Key, error) { |
| 179 // encountered error type. If multiple arguments are supplied, the resul t will | 183 return newKeyObjErr(GetKeyContext(c), getMGS(src)) |
| 180 // be a MultiError whose error index corresponds to the argument in whic h the | 184 } |
| 181 // error was encountered. | 185 |
| 182 // | 186 // MakeKey is a convenience method for manufacturing a *Key. It should only be |
| 183 // If an ent argument is a slice, its error type will be a MultiError. N ote | 187 // used when elems... is known statically (e.g. in the code) to be correct. |
| 184 // that in the scenario, where multiple slices are provided, this will r eturn a | 188 // |
| 185 // MultiError containing a nested MultiError for each slice argument. | 189 // elems is pairs of (string, string|int|int32|int64) pairs, which correspond |
| 186 Exists(ent ...interface{}) (*ExistsResult, error) | 190 // to Kind/id pairs. Example: |
| 187 | 191 // dstore.MakeKey("Parent", 1, "Child", "id") |
| 188 // Does a GetMulti for thes keys and returns true iff they exist. Will o nly | 192 // |
| 189 // return an error if it's not ErrNoSuchEntity. This is slightly more | 193 // Would create the key: |
| 190 // efficient than using Get directly, because it uses the underlying | 194 // <current appID>:<current Namespace>:/Parent,1/Child,id |
| 191 // RawInterface to avoid some reflection and copies. | 195 // |
| 192 // | 196 // If elems is not parsable (e.g. wrong length, wrong types, etc.) this method |
| 193 // If an error is encountered, the returned error will be a MultiError w hose | 197 // will panic. |
| 194 // error index corresponds to the key for which the error was encountere d. | 198 func MakeKey(c context.Context, elems ...interface{}) *Key { |
| 195 // | 199 kc := GetKeyContext(c) |
| 196 // NOTE: ExistsMulti is obsolete. The vararg-accepting Exists should be used | 200 return kc.MakeKey(elems...) |
| 197 // instead. This is left for backwards compatibility, but will be remove d from | 201 } |
| 198 // this interface at some point in the future. | 202 |
| 199 ExistsMulti(k []*Key) (BoolList, error) | 203 // NewKey constructs a new key in the current appID/Namespace, using the |
| 200 | 204 // specified parameters. |
| 201 // Get retrieves objects from the datastore. | 205 func NewKey(c context.Context, kind, stringID string, intID int64, parent *Key) *Key { |
| 202 // | 206 kc := GetKeyContext(c) |
| 203 // Each element in dst must be one of: | 207 return kc.NewKey(kind, stringID, intID, parent) |
| 204 // - *S, where S is a struct | 208 } |
| 205 // - *P, where *P is a concrete type implementing PropertyLoadSaver | 209 |
| 206 // - []S or []*S, where S is a struct | 210 // NewIncompleteKeys allocates count incomplete keys sharing the same kind and |
| 207 // - []P or []*P, where *P is a concrete type implementing Property LoadSaver | 211 // parent. It is useful as input to AllocateIDs. |
| 208 // - []I, where I is some interface type. Each element of the slice must | 212 func NewIncompleteKeys(c context.Context, count int, kind string, parent *Key) ( keys []*Key) { |
| 209 // be non-nil, and its underlying type must be either *S or *P. | 213 kc := GetKeyContext(c) |
| 210 // | 214 if count > 0 { |
| 211 // If an error is encountered, the returned error value will depend on t he | 215 keys = make([]*Key, count) |
| 212 // input arguments. If one argument is supplied, the result will be the | 216 for i := range keys { |
| 213 // encountered error type. If multiple arguments are supplied, the resul t will | 217 keys[i] = kc.NewKey(kind, "", 0, parent) |
| 214 // be a MultiError whose error index corresponds to the argument in whic h the | 218 } |
| 215 // error was encountered. | 219 } |
| 216 // | 220 return |
| 217 // If a dst argument is a slice, its error type will be a MultiError. No te | 221 } |
| 218 // that in the scenario where multiple slices are provided, this will re turn a | 222 |
| 219 // MultiError containing a nested MultiError for each slice argument. | 223 // NewKeyToks constructs a new key in the current appID/Namespace, using the |
| 220 Get(dst ...interface{}) error | 224 // specified key tokens. |
| 221 | 225 func NewKeyToks(c context.Context, toks []KeyTok) *Key { |
| 222 // GetMulti retrieves items from the datastore. | 226 kc := GetKeyContext(c) |
| 223 // | 227 return kc.NewKeyToks(toks) |
| 224 // dst must be one of: | 228 } |
| 225 // - []S or []*S, where S is a struct | 229 |
| 226 // - []P or []*P, where *P is a concrete type implementing PropertyLoa dSaver | 230 // PopulateKey loads key into obj. |
| 227 // - []I, where I is some interface type. Each element of the slice mu st | 231 // |
| 228 // be non-nil, and its underlying type must be either *S or *P. | 232 // obj is any object that Interface.Get is able to accept. |
| 229 // | 233 // |
| 230 // NOTE: GetMulti is obsolete. The vararg-accepting Get should be used | 234 // Upon successful application, this method will return true. If the key could |
| 231 // instead. This is left for backwards compatibility, but will be remove d from | 235 // not be applied to the object, this method will return false. It will panic if |
| 232 // this interface at some point in the future. | 236 // obj is an invalid datastore model. |
| 233 GetMulti(dst interface{}) error | 237 // |
| 234 | 238 // This method will panic if obj is an invalid datastore model. If the key could |
| 235 // Put writes objects into the datastore. | 239 // not be applied to the object, nothing will happen. |
| 236 // | 240 func PopulateKey(obj interface{}, key *Key) bool { |
| 237 // src must be one of: | 241 return populateKeyMGS(getMGS(obj), key) |
| 238 // - *S, where S is a struct | 242 } |
| 239 // - *P, where *P is a concrete type implementing PropertyLoadSaver | 243 |
| 240 // - []S or []*S, where S is a struct | 244 func populateKeyMGS(mgs MetaGetterSetter, key *Key) bool { |
| 241 // - []P or []*P, where *P is a concrete type implementing Property LoadSaver | 245 if mgs.SetMeta("key", key) { |
| 242 // - []I, where I is some interface type. Each element of the slice must | 246 return true |
| 243 // be non-nil, and its underlying type must be either *S or *P. | 247 } |
| 244 // | 248 |
| 245 // A *Key will be extracted from src via KeyForObj. If | 249 lst := key.LastTok() |
| 246 // extractedKey.Incomplete() is true, then Put will write the resolved ( i.e. | 250 if lst.StringID != "" { |
| 247 // automatic datastore-populated) *Key back to src. | 251 if !mgs.SetMeta("id", lst.StringID) { |
| 248 // | 252 return false |
| 249 // If an error is encountered, the returned error value will depend on t he | 253 } |
| 250 // input arguments. If one argument is supplied, the result will be the | 254 } else { |
| 251 // encountered error type. If multiple arguments are supplied, the resul t will | 255 if !mgs.SetMeta("id", lst.IntID) { |
| 252 // be a MultiError whose error index corresponds to the argument in whic h the | 256 return false |
| 253 // error was encountered. | 257 } |
| 254 // | 258 } |
| 255 // If a src argument is a slice, its error type will be a MultiError. No te | 259 |
| 256 // that in the scenario where multiple slices are provided, this will re turn a | 260 mgs.SetMeta("kind", lst.Kind) |
| 257 // MultiError containing a nested MultiError for each slice argument. | 261 mgs.SetMeta("parent", key.Parent()) |
| 258 Put(src ...interface{}) error | 262 return true |
| 259 | 263 } |
| 260 // PutMulti writes items to the datastore. | 264 |
| 261 // | 265 // RunInTransaction runs f inside of a transaction. See the appengine SDK's |
| 262 // src must be one of: | 266 // documentation for full details on the behavior of transactions in the |
| 263 // - []S or []*S, where S is a struct | 267 // datastore. |
| 264 // - []P or []*P, where *P is a concrete type implementing Property LoadSaver | 268 // |
| 265 // - []I, where I is some interface type. Each element of the slice must | 269 // Note that the behavior of transactions may change depending on what filters |
| 266 // be non-nil, and its underlying type must be either *S or *P. | 270 // have been installed. It's possible that we'll end up implementing things |
| 267 // | 271 // like nested/buffered transactions as filters. |
| 268 // If items in src resolve to Incomplete keys, PutMulti will write the | 272 func RunInTransaction(c context.Context, f func(c context.Context) error, opts * TransactionOptions) error { |
| 269 // resolved keys back to the items in src. | 273 return Raw(c).RunInTransaction(f, opts) |
| 270 // | 274 } |
| 271 // NOTE: PutMulti is obsolete. The vararg-accepting Put should be used | 275 |
| 272 // instead. This is left for backwards compatibility, but will be remove d from | 276 // Run executes the given query, and calls `cb` for each successfully |
| 273 // this interface at some point in the future. | 277 // retrieved item. |
| 274 PutMulti(src interface{}) error | 278 // |
| 275 | 279 // cb is a callback function whose signature is |
| 276 // Delete removes the supplied entities from the datastore. | 280 // func(obj TYPE[, getCursor CursorCB]) [error] |
| 277 // | 281 // |
| 278 // ent must be one of: | 282 // Where TYPE is one of: |
| 279 // - *S, where S is a struct | 283 // - S or *S, where S is a struct |
| 280 // - *P, where *P is a concrete type implementing PropertyLoadSaver | 284 // - P or *P, where *P is a concrete type implementing PropertyLoadSaver |
| 281 // - []S or []*S, where S is a struct | 285 // - *Key (implies a keys-only query) |
| 282 // - []P or []*P, where *P is a concrete type implementing Property LoadSaver | 286 // |
| 283 // - []I, where I is some interface type. Each element of the slice must | 287 // If the error is omitted from the signature, this will run until the query |
| 284 // be non-nil, and its underlying type must be either *S or *P. | 288 // returns all its results, or has an error/times out. |
| 285 // - *Key, to remove a specific key from the datastore. | 289 // |
| 286 // - []*Key, to remove a slice of keys from the datastore. | 290 // If error is in the signature, the query will continue as long as the |
| 287 // | 291 // callback returns nil. If it returns `Stop`, the query will stop and Run |
| 288 // If an error is encountered, the returned error value will depend on t he | 292 // will return nil. Otherwise, the query will stop and Run will return the |
| 289 // input arguments. If one argument is supplied, the result will be the | 293 // user's error. |
| 290 // encountered error type. If multiple arguments are supplied, the resul t will | 294 // |
| 291 // be a MultiError whose error index corresponds to the argument in whic h the | 295 // Run may also stop on the first datastore error encountered, which can occur |
| 292 // error was encountered. | 296 // due to flakiness, timeout, etc. If it encounters such an error, it will |
| 293 // | 297 // be returned. |
| 294 // If an ent argument is a slice, its error type will be a MultiError. N ote | 298 func Run(c context.Context, q *Query, cb interface{}) error { |
| 295 // that in the scenario where multiple slices are provided, this will re turn a | 299 isKey, hasErr, hasCursorCB, mat := runParseCallback(cb) |
| 296 // MultiError containing a nested MultiError for each slice argument. | 300 |
| 297 Delete(ent ...interface{}) error | 301 if isKey { |
| 298 | 302 q = q.KeysOnly(true) |
| 299 // DeleteMulti removes keys from the datastore. | 303 } |
| 300 // | 304 fq, err := q.Finalize() |
| 301 // If an error is encountered, the returned error will be a MultiError w hose | 305 if err != nil { |
| 302 // error index corresponds to the key for which the error was encountere d. | 306 return err |
| 303 // | 307 } |
| 304 // NOTE: DeleteMulti is obsolete. The vararg-accepting Delete should be used | 308 |
| 305 // instead. This is left for backwards compatibility, but will be remove d from | 309 cbVal := reflect.ValueOf(cb) |
| 306 // this interface at some point in the future. | 310 var cbFunc func(reflect.Value, CursorCB) error |
| 307 DeleteMulti(keys []*Key) error | 311 switch { |
| 308 | 312 case hasErr && hasCursorCB: |
| 309 // Testable returns the Testable interface for the implementation, or ni l if | 313 cbFunc = func(v reflect.Value, cb CursorCB) error { |
| 310 // there is none. | 314 err := cbVal.Call([]reflect.Value{v, reflect.ValueOf(cb) })[0].Interface() |
| 311 Testable() Testable | 315 if err != nil { |
| 312 | 316 return err.(error) |
| 313 // Raw returns the underlying RawInterface. The Interface and RawInterfa ce may | 317 } |
| 314 // be used interchangably; there's no danger of interleaving access to t he | 318 return nil |
| 315 // datastore via the two. | 319 } |
| 316 Raw() RawInterface | 320 |
| 317 } | 321 case hasErr && !hasCursorCB: |
| 322 cbFunc = func(v reflect.Value, _ CursorCB) error { | |
| 323 err := cbVal.Call([]reflect.Value{v})[0].Interface() | |
| 324 if err != nil { | |
| 325 return err.(error) | |
| 326 } | |
| 327 return nil | |
| 328 } | |
| 329 | |
| 330 case !hasErr && hasCursorCB: | |
| 331 cbFunc = func(v reflect.Value, cb CursorCB) error { | |
| 332 cbVal.Call([]reflect.Value{v, reflect.ValueOf(cb)}) | |
| 333 return nil | |
| 334 } | |
| 335 | |
| 336 case !hasErr && !hasCursorCB: | |
| 337 cbFunc = func(v reflect.Value, _ CursorCB) error { | |
| 338 cbVal.Call([]reflect.Value{v}) | |
| 339 return nil | |
| 340 } | |
| 341 } | |
| 342 | |
| 343 raw := Raw(c) | |
| 344 if isKey { | |
| 345 err = raw.Run(fq, func(k *Key, _ PropertyMap, gc CursorCB) error { | |
| 346 return cbFunc(reflect.ValueOf(k), gc) | |
| 347 }) | |
| 348 } else { | |
| 349 err = raw.Run(fq, func(k *Key, pm PropertyMap, gc CursorCB) erro r { | |
| 350 itm := mat.newElem() | |
| 351 if err := mat.setPM(itm, pm); err != nil { | |
| 352 return err | |
| 353 } | |
| 354 mat.setKey(itm, k) | |
| 355 return cbFunc(itm, gc) | |
| 356 }) | |
| 357 } | |
| 358 return filterStop(err) | |
| 359 } | |
| 360 | |
| 361 // Count executes the given query and returns the number of entries which | |
| 362 // match it. | |
| 363 func Count(c context.Context, q *Query) (int64, error) { | |
| 364 fq, err := q.Finalize() | |
| 365 if err != nil { | |
| 366 return 0, err | |
| 367 } | |
| 368 v, err := Raw(c).Count(fq) | |
| 369 return v, filterStop(err) | |
| 370 } | |
| 371 | |
| 372 // DecodeCursor converts a string returned by a Cursor into a Cursor instance. | |
| 373 // It will return an error if the supplied string is not valid, or could not | |
| 374 // be decoded by the implementation. | |
| 375 func DecodeCursor(c context.Context, s string) (Cursor, error) { | |
| 376 return Raw(c).DecodeCursor(s) | |
| 377 } | |
| 378 | |
| 379 // GetAll retrieves all of the Query results into dst. | |
| 380 // | |
| 381 // dst must be one of: | |
| 382 // - *[]S or *[]*S, where S is a struct | |
| 383 // - *[]P or *[]*P, where *P is a concrete type implementing | |
| 384 // PropertyLoadSaver | |
| 385 // - *[]*Key implies a keys-only query. | |
| 386 func GetAll(c context.Context, q *Query, dst interface{}) error { | |
| 387 v := reflect.ValueOf(dst) | |
| 388 if v.Kind() != reflect.Ptr { | |
| 389 panic(fmt.Errorf("invalid GetAll dst: must have a ptr-to-slice: %T", dst)) | |
| 390 } | |
| 391 if !v.IsValid() || v.IsNil() { | |
| 392 panic(errors.New("invalid GetAll dst: <nil>")) | |
| 393 } | |
| 394 | |
| 395 raw := Raw(c) | |
| 396 if keys, ok := dst.(*[]*Key); ok { | |
| 397 fq, err := q.KeysOnly(true).Finalize() | |
| 398 if err != nil { | |
| 399 return err | |
| 400 } | |
| 401 | |
| 402 return raw.Run(fq, func(k *Key, _ PropertyMap, _ CursorCB) error { | |
| 403 *keys = append(*keys, k) | |
| 404 return nil | |
| 405 }) | |
| 406 } | |
| 407 fq, err := q.Finalize() | |
| 408 if err != nil { | |
| 409 return err | |
| 410 } | |
| 411 | |
| 412 slice := v.Elem() | |
| 413 mat := mustParseMultiArg(slice.Type()) | |
| 414 if mat.newElem == nil { | |
| 415 panic(fmt.Errorf("invalid GetAll dst (non-concrete element type) : %T", dst)) | |
| 416 } | |
| 417 | |
| 418 errs := map[int]error{} | |
| 419 i := 0 | |
| 420 err = filterStop(raw.Run(fq, func(k *Key, pm PropertyMap, _ CursorCB) er ror { | |
| 421 slice.Set(reflect.Append(slice, mat.newElem())) | |
| 422 itm := slice.Index(i) | |
| 423 mat.setKey(itm, k) | |
| 424 err := mat.setPM(itm, pm) | |
| 425 if err != nil { | |
| 426 errs[i] = err | |
| 427 } | |
| 428 i++ | |
| 429 return nil | |
| 430 })) | |
| 431 if err == nil { | |
| 432 if len(errs) > 0 { | |
| 433 me := make(errors.MultiError, slice.Len()) | |
| 434 for i, e := range errs { | |
| 435 me[i] = e | |
| 436 } | |
| 437 err = me | |
| 438 } | |
| 439 } | |
| 440 return err | |
| 441 } | |
| 442 | |
| 443 // Exists tests if the supplied objects are present in the datastore. | |
| 444 // | |
| 445 // ent must be one of: | |
| 446 // - *S, where S is a struct | |
| 447 // - *P, where *P is a concrete type implementing PropertyLoadSaver | |
| 448 // - []S or []*S, where S is a struct | |
| 449 // - []P or []*P, where *P is a concrete type implementing PropertyLoadSave r | |
| 450 // - []I, where I is some interface type. Each element of the slice must | |
| 451 // be non-nil, and its underlying type must be either *S or *P. | |
| 452 // - *Key, to check a specific key from the datastore. | |
| 453 // - []*Key, to check a slice of keys from the datastore. | |
| 454 // | |
| 455 // If an error is encountered, the returned error value will depend on the | |
| 456 // input arguments. If one argument is supplied, the result will be the | |
| 457 // encountered error type. If multiple arguments are supplied, the result will | |
| 458 // be a MultiError whose error index corresponds to the argument in which the | |
| 459 // error was encountered. | |
| 460 // | |
| 461 // If an ent argument is a slice, its error type will be a MultiError. Note | |
| 462 // that in the scenario, where multiple slices are provided, this will return a | |
| 463 // MultiError containing a nested MultiError for each slice argument. | |
| 464 func Exists(c context.Context, ent ...interface{}) (*ExistsResult, error) { | |
| 465 if len(ent) == 0 { | |
| 466 return nil, nil | |
| 467 } | |
| 468 | |
| 469 mma, err := makeMetaMultiArg(ent, mmaKeysOnly) | |
| 470 if err != nil { | |
| 471 panic(err) | |
| 472 } | |
| 473 | |
| 474 keys, _, err := mma.getKeysPMs(GetKeyContext(c), false) | |
| 475 if err != nil { | |
| 476 return nil, err | |
| 477 } | |
| 478 if len(keys) == 0 { | |
| 479 return nil, nil | |
| 480 } | |
| 481 | |
| 482 var bt boolTracker | |
| 483 it := mma.iterator(bt.init(mma)) | |
| 484 err = filterStop(Raw(c).GetMulti(keys, nil, func(_ PropertyMap, err erro r) error { | |
| 485 it.next(func(*multiArgType, reflect.Value) error { | |
| 486 return err | |
| 487 }) | |
| 488 return nil | |
| 489 })) | |
| 490 if err == nil { | |
| 491 err = bt.error() | |
| 492 | |
| 493 if err != nil && len(ent) == 1 { | |
| 494 // Single-argument Exists will return a single error. | |
| 495 err = errors.SingleError(err) | |
| 496 } | |
| 497 } | |
| 498 return bt.result(), err | |
| 499 } | |
| 500 | |
| 501 // Get retrieves objects from the datastore. | |
| 502 // | |
| 503 // Each element in dst must be one of: | |
| 504 // - *S, where S is a struct | |
| 505 // - *P, where *P is a concrete type implementing PropertyLoadSaver | |
| 506 // - []S or []*S, where S is a struct | |
| 507 // - []P or []*P, where *P is a concrete type implementing PropertyLoadSave r | |
| 508 // - []I, where I is some interface type. Each element of the slice must | |
| 509 // be non-nil, and its underlying type must be either *S or *P. | |
| 510 // | |
| 511 // If an error is encountered, the returned error value will depend on the | |
| 512 // input arguments. If one argument is supplied, the result will be the | |
| 513 // encountered error type. If multiple arguments are supplied, the result will | |
| 514 // be a MultiError whose error index corresponds to the argument in which the | |
| 515 // error was encountered. | |
| 516 // | |
| 517 // If a dst argument is a slice, its error type will be a MultiError. Note | |
| 518 // that in the scenario where multiple slices are provided, this will return a | |
| 519 // MultiError containing a nested MultiError for each slice argument. | |
| 520 func Get(c context.Context, dst ...interface{}) error { | |
| 521 if len(dst) == 0 { | |
| 522 return nil | |
| 523 } | |
| 524 | |
| 525 mma, err := makeMetaMultiArg(dst, mmaReadWrite) | |
| 526 if err != nil { | |
| 527 panic(err) | |
| 528 } | |
| 529 | |
| 530 keys, pms, err := mma.getKeysPMs(GetKeyContext(c), true) | |
| 531 if err != nil { | |
| 532 return err | |
| 533 } | |
| 534 if len(keys) == 0 { | |
| 535 return nil | |
| 536 } | |
| 537 | |
| 538 var et errorTracker | |
| 539 it := mma.iterator(et.init(mma)) | |
| 540 meta := NewMultiMetaGetter(pms) | |
| 541 err = filterStop(Raw(c).GetMulti(keys, meta, func(pm PropertyMap, err er ror) error { | |
| 542 it.next(func(mat *multiArgType, slot reflect.Value) error { | |
| 543 if err != nil { | |
| 544 return err | |
| 545 } | |
| 546 return mat.setPM(slot, pm) | |
| 547 }) | |
| 548 return nil | |
| 549 })) | |
| 550 | |
| 551 if err == nil { | |
| 552 err = et.error() | |
| 553 | |
| 554 if err != nil && len(dst) == 1 { | |
| 555 // Single-argument Get will return a single error. | |
| 556 err = errors.SingleError(err) | |
| 557 } | |
| 558 } | |
| 559 return err | |
| 560 } | |
| 561 | |
| 562 // Put writes objects into the datastore. | |
| 563 // | |
| 564 // src must be one of: | |
| 565 // - *S, where S is a struct | |
| 566 // - *P, where *P is a concrete type implementing PropertyLoadSaver | |
| 567 // - []S or []*S, where S is a struct | |
| 568 // - []P or []*P, where *P is a concrete type implementing PropertyLoadSave r | |
| 569 // - []I, where I is some interface type. Each element of the slice must | |
| 570 // be non-nil, and its underlying type must be either *S or *P. | |
| 571 // | |
| 572 // A *Key will be extracted from src via KeyForObj. If | |
| 573 // extractedKey.Incomplete() is true, then Put will write the resolved (i.e. | |
| 574 // automatic datastore-populated) *Key back to src. | |
| 575 // | |
| 576 // If an error is encountered, the returned error value will depend on the | |
| 577 // input arguments. If one argument is supplied, the result will be the | |
| 578 // encountered error type. If multiple arguments are supplied, the result will | |
| 579 // be a MultiError whose error index corresponds to the argument in which the | |
| 580 // error was encountered. | |
| 581 // | |
| 582 // If a src argument is a slice, its error type will be a MultiError. Note | |
| 583 // that in the scenario where multiple slices are provided, this will return a | |
| 584 // MultiError containing a nested MultiError for each slice argument. | |
| 585 func Put(c context.Context, src ...interface{}) error { | |
| 586 if len(src) == 0 { | |
| 587 return nil | |
| 588 } | |
| 589 | |
| 590 mma, err := makeMetaMultiArg(src, mmaReadWrite) | |
| 591 if err != nil { | |
| 592 panic(err) | |
| 593 } | |
| 594 | |
| 595 keys, vals, err := mma.getKeysPMs(GetKeyContext(c), false) | |
| 596 if err != nil { | |
| 597 return err | |
| 598 } | |
| 599 if len(keys) == 0 { | |
| 600 return nil | |
| 601 } | |
| 602 | |
| 603 i := 0 | |
| 604 var et errorTracker | |
| 605 it := mma.iterator(et.init(mma)) | |
| 606 err = filterStop(Raw(c).PutMulti(keys, vals, func(key *Key, err error) e rror { | |
| 607 it.next(func(mat *multiArgType, slot reflect.Value) error { | |
| 608 if err != nil { | |
| 609 return err | |
| 610 } | |
| 611 if key != keys[i] { | |
| 612 mat.setKey(slot, key) | |
| 613 } | |
| 614 return nil | |
| 615 }) | |
| 616 | |
| 617 i++ | |
| 618 return nil | |
| 619 })) | |
| 620 | |
| 621 if err == nil { | |
| 622 err = et.error() | |
| 623 | |
| 624 if err != nil && len(src) == 1 { | |
| 625 // Single-argument Put will return a single error. | |
| 626 err = errors.SingleError(err) | |
| 627 } | |
| 628 } | |
| 629 return err | |
| 630 } | |
| 631 | |
| 632 // Delete removes the supplied entities from the datastore. | |
| 633 // | |
| 634 // ent must be one of: | |
| 635 // - *S, where S is a struct | |
| 636 // - *P, where *P is a concrete type implementing PropertyLoadSaver | |
| 637 // - []S or []*S, where S is a struct | |
| 638 // - []P or []*P, where *P is a concrete type implementing PropertyLoadSave r | |
| 639 // - []I, where I is some interface type. Each element of the slice must | |
| 640 // be non-nil, and its underlying type must be either *S or *P. | |
| 641 // - *Key, to remove a specific key from the datastore. | |
| 642 // - []*Key, to remove a slice of keys from the datastore. | |
| 643 // | |
| 644 // If an error is encountered, the returned error value will depend on the | |
| 645 // input arguments. If one argument is supplied, the result will be the | |
| 646 // encountered error type. If multiple arguments are supplied, the result will | |
| 647 // be a MultiError whose error index corresponds to the argument in which the | |
| 648 // error was encountered. | |
| 649 // | |
| 650 // If an ent argument is a slice, its error type will be a MultiError. Note | |
| 651 // that in the scenario where multiple slices are provided, this will return a | |
| 652 // MultiError containing a nested MultiError for each slice argument. | |
| 653 func Delete(c context.Context, ent ...interface{}) error { | |
| 654 if len(ent) == 0 { | |
| 655 return nil | |
| 656 } | |
| 657 | |
| 658 mma, err := makeMetaMultiArg(ent, mmaKeysOnly) | |
| 659 if err != nil { | |
| 660 panic(err) | |
| 661 } | |
| 662 | |
| 663 keys, _, err := mma.getKeysPMs(GetKeyContext(c), false) | |
| 664 if err != nil { | |
| 665 return err | |
| 666 } | |
| 667 if len(keys) == 0 { | |
| 668 return nil | |
| 669 } | |
| 670 | |
| 671 var et errorTracker | |
| 672 it := mma.iterator(et.init(mma)) | |
| 673 err = filterStop(Raw(c).DeleteMulti(keys, func(err error) error { | |
| 674 it.next(func(*multiArgType, reflect.Value) error { | |
| 675 return err | |
| 676 }) | |
| 677 | |
| 678 return nil | |
| 679 })) | |
| 680 if err == nil { | |
| 681 err = et.error() | |
| 682 | |
| 683 if err != nil && len(ent) == 1 { | |
| 684 // Single-argument Delete will return a single error. | |
| 685 err = errors.SingleError(err) | |
| 686 } | |
| 687 } | |
| 688 return err | |
| 689 } | |
| 690 | |
| 691 // GetTestable returns the Testable interface for the implementation, or nil if | |
| 692 // there is none. | |
| 693 func GetTestable(c context.Context) Testable { | |
| 694 return Raw(c).GetTestable() | |
| 695 } | |
| 696 | |
| 697 func filterStop(err error) error { | |
| 698 if err == Stop { | |
| 699 err = nil | |
| 700 } | |
| 701 return err | |
| 702 } | |
| OLD | NEW |