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

Side by Side Diff: service/datastore/interface.go

Issue 2302743002: Interface update, per-method Contexts. (Closed)
Patch Set: Lightning talk licenses. Created 4 years, 3 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 unified diff | Download patch
« no previous file with comments | « service/datastore/finalized_query.go ('k') | service/datastore/key.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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 {
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 maybeSingleError(err, ent)
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 return maybeSingleError(err, ent)
134 // If the error is omitted from the signature, this will run until the q uery 138 }
135 // returns all its results, or has an error/times out. 139
136 // 140 // KeyForObj extracts a key from src.
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 // It is the same as KeyForObjErr, except that if KeyForObjErr would have
139 // will return nil. Otherwise, the query will stop and Run will return t he 143 // returned an error, this method panics. It's safe to use if you know that
140 // user's error. 144 // src statically meets the metadata constraints described by KeyForObjErr.
141 // 145 func KeyForObj(c context.Context, src interface{}) *Key {
142 // Run may also stop on the first datastore error encountered, which can occur 146 ret, err := KeyForObjErr(c, src)
143 // due to flakiness, timeout, etc. If it encounters such an error, it wi ll 147 if err != nil {
144 // be returned. 148 panic(err)
145 Run(q *Query, cb interface{}) error 149 }
146 150 return ret
147 // Count executes the given query and returns the number of entries whic h 151 }
148 // match it. 152
149 Count(q *Query) (int64, error) 153 // KeyForObjErr extracts a key from src.
150 154 //
151 // DecodeCursor converts a string returned by a Cursor into a Cursor ins tance. 155 // src must be one of:
152 // It will return an error if the supplied string is not valid, or could not 156 // - *S, where S is a struct
153 // be decoded by the implementation. 157 // - a PropertyLoadSaver
154 DecodeCursor(string) (Cursor, error) 158 //
155 159 // It is expected that the struct exposes the following metadata (as retrieved
156 // GetAll retrieves all of the Query results into dst. 160 // by MetaGetter.GetMeta):
157 // 161 // - "key" (type: Key) - The full datastore key to use. Must not be nil.
158 // dst must be one of: 162 // OR
159 // - *[]S or *[]*S, where S is a struct 163 // - "id" (type: int64 or string) - The id of the Key to create
160 // - *[]P or *[]*P, where *P is a concrete type implementing 164 // - "kind" (optional, type: string) - The kind of the Key to create. If
161 // PropertyLoadSaver 165 // blank or not present, KeyForObjErr will extract the name of the src
162 // - *[]*Key implies a keys-only query. 166 // object's type.
163 GetAll(q *Query, dst interface{}) error 167 // - "parent" (optional, type: Key) - The parent key to use.
164 168 //
165 // Exists tests if the supplied objects are present in the datastore. 169 // By default, the metadata will be extracted from the struct and its tagged
166 // 170 // properties. However, if the struct implements MetaGetterSetter it is
167 // ent must be one of: 171 // wholly responsible for exporting the required fields. A struct that
168 // - *S, where S is a struct 172 // implements GetMeta to make some minor tweaks can evoke the defualt behavior
169 // - *P, where *P is a concrete type implementing PropertyLoadSaver 173 // by using GetPLS(s).GetMeta.
170 // - []S or []*S, where S is a struct 174 //
171 // - []P or []*P, where *P is a concrete type implementing Property LoadSaver 175 // If a required metadata item is missing or of the wrong type, then this will
172 // - []I, where I is some interface type. Each element of the slice must 176 // return an error.
173 // be non-nil, and its underlying type must be either *S or *P. 177 func KeyForObjErr(c context.Context, src interface{}) (*Key, error) {
174 // - *Key, to check a specific key from the datastore. 178 return newKeyObjErr(GetKeyContext(c), getMGS(src))
175 // - []*Key, to check a slice of keys from the datastore. 179 }
176 // 180
177 // If an error is encountered, the returned error value will depend on t he 181 // MakeKey is a convenience method for manufacturing a *Key. It should only be
178 // input arguments. If one argument is supplied, the result will be the 182 // used when elems... is known statically (e.g. in the code) to be correct.
179 // encountered error type. If multiple arguments are supplied, the resul t will 183 //
180 // be a MultiError whose error index corresponds to the argument in whic h the 184 // elems is pairs of (string, string|int|int32|int64) pairs, which correspond
181 // error was encountered. 185 // to Kind/id pairs. Example:
182 // 186 // dstore.MakeKey("Parent", 1, "Child", "id")
183 // If an ent argument is a slice, its error type will be a MultiError. N ote 187 //
184 // that in the scenario, where multiple slices are provided, this will r eturn a 188 // Would create the key:
185 // MultiError containing a nested MultiError for each slice argument. 189 // <current appID>:<current Namespace>:/Parent,1/Child,id
186 Exists(ent ...interface{}) (*ExistsResult, error) 190 //
187 191 // If elems is not parsable (e.g. wrong length, wrong types, etc.) this method
188 // Does a GetMulti for thes keys and returns true iff they exist. Will o nly 192 // will panic.
189 // return an error if it's not ErrNoSuchEntity. This is slightly more 193 func MakeKey(c context.Context, elems ...interface{}) *Key {
190 // efficient than using Get directly, because it uses the underlying 194 kc := GetKeyContext(c)
191 // RawInterface to avoid some reflection and copies. 195 return kc.MakeKey(elems...)
192 // 196 }
193 // If an error is encountered, the returned error will be a MultiError w hose 197
194 // error index corresponds to the key for which the error was encountere d. 198 // NewKey constructs a new key in the current appID/Namespace, using the
195 // 199 // specified parameters.
196 // NOTE: ExistsMulti is obsolete. The vararg-accepting Exists should be used 200 func NewKey(c context.Context, kind, stringID string, intID int64, parent *Key) *Key {
197 // instead. This is left for backwards compatibility, but will be remove d from 201 kc := GetKeyContext(c)
198 // this interface at some point in the future. 202 return kc.NewKey(kind, stringID, intID, parent)
199 ExistsMulti(k []*Key) (BoolList, error) 203 }
200 204
201 // Get retrieves objects from the datastore. 205 // NewIncompleteKeys allocates count incomplete keys sharing the same kind and
202 // 206 // parent. It is useful as input to AllocateIDs.
203 // Each element in dst must be one of: 207 func NewIncompleteKeys(c context.Context, count int, kind string, parent *Key) ( keys []*Key) {
204 // - *S, where S is a struct 208 kc := GetKeyContext(c)
205 // - *P, where *P is a concrete type implementing PropertyLoadSaver 209 if count > 0 {
206 // - []S or []*S, where S is a struct 210 keys = make([]*Key, count)
207 // - []P or []*P, where *P is a concrete type implementing Property LoadSaver 211 for i := range keys {
208 // - []I, where I is some interface type. Each element of the slice must 212 keys[i] = kc.NewKey(kind, "", 0, parent)
209 // be non-nil, and its underlying type must be either *S or *P. 213 }
210 // 214 }
211 // If an error is encountered, the returned error value will depend on t he 215 return
212 // input arguments. If one argument is supplied, the result will be the 216 }
213 // encountered error type. If multiple arguments are supplied, the resul t will 217
214 // be a MultiError whose error index corresponds to the argument in whic h the 218 // NewKeyToks constructs a new key in the current appID/Namespace, using the
215 // error was encountered. 219 // specified key tokens.
216 // 220 func NewKeyToks(c context.Context, toks []KeyTok) *Key {
217 // If a dst argument is a slice, its error type will be a MultiError. No te 221 kc := GetKeyContext(c)
218 // that in the scenario where multiple slices are provided, this will re turn a 222 return kc.NewKeyToks(toks)
219 // MultiError containing a nested MultiError for each slice argument. 223 }
220 Get(dst ...interface{}) error 224
221 225 // PopulateKey loads key into obj.
222 // GetMulti retrieves items from the datastore. 226 //
223 // 227 // obj is any object that Interface.Get is able to accept.
224 // dst must be one of: 228 //
225 // - []S or []*S, where S is a struct 229 // Upon successful application, this method will return true. If the key could
226 // - []P or []*P, where *P is a concrete type implementing PropertyLoa dSaver 230 // not be applied to the object, this method will return false. It will panic if
227 // - []I, where I is some interface type. Each element of the slice mu st 231 // obj is an invalid datastore model.
228 // be non-nil, and its underlying type must be either *S or *P. 232 //
229 // 233 // This method will panic if obj is an invalid datastore model. If the key could
230 // NOTE: GetMulti is obsolete. The vararg-accepting Get should be used 234 // not be applied to the object, nothing will happen.
231 // instead. This is left for backwards compatibility, but will be remove d from 235 func PopulateKey(obj interface{}, key *Key) bool {
232 // this interface at some point in the future. 236 return populateKeyMGS(getMGS(obj), key)
233 GetMulti(dst interface{}) error 237 }
234 238
235 // Put writes objects into the datastore. 239 func populateKeyMGS(mgs MetaGetterSetter, key *Key) bool {
236 // 240 if mgs.SetMeta("key", key) {
237 // src must be one of: 241 return true
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 lst := key.LastTok()
241 // - []P or []*P, where *P is a concrete type implementing Property LoadSaver 245 if lst.StringID != "" {
242 // - []I, where I is some interface type. Each element of the slice must 246 if !mgs.SetMeta("id", lst.StringID) {
243 // be non-nil, and its underlying type must be either *S or *P. 247 return false
244 // 248 }
245 // A *Key will be extracted from src via KeyForObj. If 249 } else {
246 // extractedKey.Incomplete() is true, then Put will write the resolved ( i.e. 250 if !mgs.SetMeta("id", lst.IntID) {
247 // automatic datastore-populated) *Key back to src. 251 return false
248 // 252 }
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
251 // encountered error type. If multiple arguments are supplied, the resul t will 255 mgs.SetMeta("kind", lst.Kind)
252 // be a MultiError whose error index corresponds to the argument in whic h the 256 mgs.SetMeta("parent", key.Parent())
253 // error was encountered. 257 return true
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 // RunInTransaction runs f inside of a transaction. See the appengine SDK's
257 // MultiError containing a nested MultiError for each slice argument. 261 // documentation for full details on the behavior of transactions in the
258 Put(src ...interface{}) error 262 // datastore.
259 263 //
260 // PutMulti writes items to the datastore. 264 // Note that the behavior of transactions may change depending on what filters
261 // 265 // have been installed. It's possible that we'll end up implementing things
262 // src must be one of: 266 // like nested/buffered transactions as filters.
263 // - []S or []*S, where S is a struct 267 func RunInTransaction(c context.Context, f func(c context.Context) error, opts * TransactionOptions) error {
264 // - []P or []*P, where *P is a concrete type implementing Property LoadSaver 268 return Raw(c).RunInTransaction(f, opts)
265 // - []I, where I is some interface type. Each element of the slice must 269 }
266 // be non-nil, and its underlying type must be either *S or *P. 270
267 // 271 // Run executes the given query, and calls `cb` for each successfully
268 // If items in src resolve to Incomplete keys, PutMulti will write the 272 // retrieved item.
269 // resolved keys back to the items in src. 273 //
270 // 274 // cb is a callback function whose signature is
271 // NOTE: PutMulti is obsolete. The vararg-accepting Put should be used 275 // func(obj TYPE[, getCursor CursorCB]) [error]
272 // instead. This is left for backwards compatibility, but will be remove d from 276 //
273 // this interface at some point in the future. 277 // Where TYPE is one of:
274 PutMulti(src interface{}) error 278 // - S or *S, where S is a struct
275 279 // - P or *P, where *P is a concrete type implementing PropertyLoadSaver
276 // Delete removes the supplied entities from the datastore. 280 // - *Key (implies a keys-only query)
277 // 281 //
278 // ent must be one of: 282 // If the error is omitted from the signature, this will run until the query
279 // - *S, where S is a struct 283 // returns all its results, or has an error/times out.
280 // - *P, where *P is a concrete type implementing PropertyLoadSaver 284 //
281 // - []S or []*S, where S is a struct 285 // If error is in the signature, the query will continue as long as the
282 // - []P or []*P, where *P is a concrete type implementing Property LoadSaver 286 // callback returns nil. If it returns `Stop`, the query will stop and Run
283 // - []I, where I is some interface type. Each element of the slice must 287 // will return nil. Otherwise, the query will stop and Run will return the
284 // be non-nil, and its underlying type must be either *S or *P. 288 // user's error.
285 // - *Key, to remove a specific key from the datastore. 289 //
286 // - []*Key, to remove a slice of keys from the datastore. 290 // Run may also stop on the first datastore error encountered, which can occur
287 // 291 // due to flakiness, timeout, etc. If it encounters such an error, it will
288 // If an error is encountered, the returned error value will depend on t he 292 // be returned.
289 // input arguments. If one argument is supplied, the result will be the 293 func Run(c context.Context, q *Query, cb interface{}) error {
290 // encountered error type. If multiple arguments are supplied, the resul t will 294 isKey, hasErr, hasCursorCB, mat := runParseCallback(cb)
291 // be a MultiError whose error index corresponds to the argument in whic h the 295
292 // error was encountered. 296 if isKey {
293 // 297 q = q.KeysOnly(true)
294 // If an ent argument is a slice, its error type will be a MultiError. N ote 298 }
295 // that in the scenario where multiple slices are provided, this will re turn a 299 fq, err := q.Finalize()
296 // MultiError containing a nested MultiError for each slice argument. 300 if err != nil {
297 Delete(ent ...interface{}) error 301 return err
298 302 }
299 // DeleteMulti removes keys from the datastore. 303
300 // 304 cbVal := reflect.ValueOf(cb)
301 // If an error is encountered, the returned error will be a MultiError w hose 305 var cbFunc func(reflect.Value, CursorCB) error
302 // error index corresponds to the key for which the error was encountere d. 306 switch {
303 // 307 case hasErr && hasCursorCB:
304 // NOTE: DeleteMulti is obsolete. The vararg-accepting Delete should be used 308 cbFunc = func(v reflect.Value, cb CursorCB) error {
305 // instead. This is left for backwards compatibility, but will be remove d from 309 err := cbVal.Call([]reflect.Value{v, reflect.ValueOf(cb) })[0].Interface()
306 // this interface at some point in the future. 310 if err != nil {
307 DeleteMulti(keys []*Key) error 311 return err.(error)
308 312 }
309 // Testable returns the Testable interface for the implementation, or ni l if 313 return nil
310 // there is none. 314 }
311 Testable() Testable 315
312 316 case hasErr && !hasCursorCB:
313 // Raw returns the underlying RawInterface. The Interface and RawInterfa ce may 317 cbFunc = func(v reflect.Value, _ CursorCB) error {
314 // be used interchangably; there's no danger of interleaving access to t he 318 err := cbVal.Call([]reflect.Value{v})[0].Interface()
315 // datastore via the two. 319 if err != nil {
316 Raw() RawInterface 320 return err.(error)
317 } 321 }
322 return nil
323 }
324
325 case !hasErr && hasCursorCB:
326 cbFunc = func(v reflect.Value, cb CursorCB) error {
327 cbVal.Call([]reflect.Value{v, reflect.ValueOf(cb)})
328 return nil
329 }
330
331 case !hasErr && !hasCursorCB:
332 cbFunc = func(v reflect.Value, _ CursorCB) error {
333 cbVal.Call([]reflect.Value{v})
334 return nil
335 }
336 }
337
338 raw := Raw(c)
339 if isKey {
340 err = raw.Run(fq, func(k *Key, _ PropertyMap, gc CursorCB) error {
341 return cbFunc(reflect.ValueOf(k), gc)
342 })
343 } else {
344 err = raw.Run(fq, func(k *Key, pm PropertyMap, gc CursorCB) erro r {
345 itm := mat.newElem()
346 if err := mat.setPM(itm, pm); err != nil {
347 return err
348 }
349 mat.setKey(itm, k)
350 return cbFunc(itm, gc)
351 })
352 }
353 return filterStop(err)
354 }
355
356 // Count executes the given query and returns the number of entries which
357 // match it.
358 func Count(c context.Context, q *Query) (int64, error) {
359 fq, err := q.Finalize()
360 if err != nil {
361 return 0, err
362 }
363 v, err := Raw(c).Count(fq)
364 return v, filterStop(err)
365 }
366
367 // DecodeCursor converts a string returned by a Cursor into a Cursor instance.
368 // It will return an error if the supplied string is not valid, or could not
369 // be decoded by the implementation.
370 func DecodeCursor(c context.Context, s string) (Cursor, error) {
371 return Raw(c).DecodeCursor(s)
372 }
373
374 // GetAll retrieves all of the Query results into dst.
375 //
376 // dst must be one of:
377 // - *[]S or *[]*S, where S is a struct
378 // - *[]P or *[]*P, where *P is a concrete type implementing
379 // PropertyLoadSaver
380 // - *[]*Key implies a keys-only query.
381 func GetAll(c context.Context, q *Query, dst interface{}) error {
382 v := reflect.ValueOf(dst)
383 if v.Kind() != reflect.Ptr {
384 panic(fmt.Errorf("invalid GetAll dst: must have a ptr-to-slice: %T", dst))
385 }
386 if !v.IsValid() || v.IsNil() {
387 panic(errors.New("invalid GetAll dst: <nil>"))
388 }
389
390 raw := Raw(c)
391 if keys, ok := dst.(*[]*Key); ok {
392 fq, err := q.KeysOnly(true).Finalize()
393 if err != nil {
394 return err
395 }
396
397 return raw.Run(fq, func(k *Key, _ PropertyMap, _ CursorCB) error {
398 *keys = append(*keys, k)
399 return nil
400 })
401 }
402 fq, err := q.Finalize()
403 if err != nil {
404 return err
405 }
406
407 slice := v.Elem()
408 mat := mustParseMultiArg(slice.Type())
409 if mat.newElem == nil {
410 panic(fmt.Errorf("invalid GetAll dst (non-concrete element type) : %T", dst))
411 }
412
413 errs := map[int]error{}
414 i := 0
415 err = filterStop(raw.Run(fq, func(k *Key, pm PropertyMap, _ CursorCB) er ror {
416 slice.Set(reflect.Append(slice, mat.newElem()))
417 itm := slice.Index(i)
418 mat.setKey(itm, k)
419 err := mat.setPM(itm, pm)
420 if err != nil {
421 errs[i] = err
422 }
423 i++
424 return nil
425 }))
426 if err == nil {
427 if len(errs) > 0 {
428 me := make(errors.MultiError, slice.Len())
429 for i, e := range errs {
430 me[i] = e
431 }
432 err = me
433 }
434 }
435 return err
436 }
437
438 // Exists tests if the supplied objects are present in the datastore.
439 //
440 // ent must be one of:
441 // - *S, where S is a struct
442 // - *P, where *P is a concrete type implementing PropertyLoadSaver
443 // - []S or []*S, where S is a struct
444 // - []P or []*P, where *P is a concrete type implementing PropertyLoadSave r
445 // - []I, where I is some interface type. Each element of the slice must
446 // be non-nil, and its underlying type must be either *S or *P.
447 // - *Key, to check a specific key from the datastore.
448 // - []*Key, to check a slice of keys from the datastore.
449 //
450 // If an error is encountered, the returned error value will depend on the
451 // input arguments. If one argument is supplied, the result will be the
452 // encountered error type. If multiple arguments are supplied, the result will
453 // be a MultiError whose error index corresponds to the argument in which the
454 // error was encountered.
455 //
456 // If an ent argument is a slice, its error type will be a MultiError. Note
457 // that in the scenario, where multiple slices are provided, this will return a
458 // MultiError containing a nested MultiError for each slice argument.
459 func Exists(c context.Context, ent ...interface{}) (*ExistsResult, error) {
460 if len(ent) == 0 {
461 return nil, nil
462 }
463
464 mma, err := makeMetaMultiArg(ent, mmaKeysOnly)
465 if err != nil {
466 panic(err)
467 }
468
469 keys, _, err := mma.getKeysPMs(GetKeyContext(c), false)
470 if err != nil {
471 return nil, maybeSingleError(err, ent)
472 }
473 if len(keys) == 0 {
474 return nil, nil
475 }
476
477 var bt boolTracker
478 it := mma.iterator(bt.init(mma))
479 err = filterStop(Raw(c).GetMulti(keys, nil, func(_ PropertyMap, err erro r) error {
480 it.next(func(*multiArgType, reflect.Value) error {
481 return err
482 })
483 return nil
484 }))
485 if err == nil {
486 err = bt.error()
487 }
488 return bt.result(), maybeSingleError(err, ent)
489 }
490
491 // Get retrieves objects from the datastore.
492 //
493 // Each element in dst must be one of:
494 // - *S, where S is a struct
495 // - *P, where *P is a concrete type implementing PropertyLoadSaver
496 // - []S or []*S, where S is a struct
497 // - []P or []*P, where *P is a concrete type implementing PropertyLoadSave r
498 // - []I, where I is some interface type. Each element of the slice must
499 // be non-nil, and its underlying type must be either *S or *P.
500 //
501 // If an error is encountered, the returned error value will depend on the
502 // input arguments. If one argument is supplied, the result will be the
503 // encountered error type. If multiple arguments are supplied, the result will
504 // be a MultiError whose error index corresponds to the argument in which the
505 // error was encountered.
506 //
507 // If a dst argument is a slice, its error type will be a MultiError. Note
508 // that in the scenario where multiple slices are provided, this will return a
509 // MultiError containing a nested MultiError for each slice argument.
510 func Get(c context.Context, dst ...interface{}) error {
511 if len(dst) == 0 {
512 return nil
513 }
514
515 mma, err := makeMetaMultiArg(dst, mmaReadWrite)
516 if err != nil {
517 panic(err)
518 }
519
520 keys, pms, err := mma.getKeysPMs(GetKeyContext(c), true)
521 if err != nil {
522 return maybeSingleError(err, dst)
523 }
524 if len(keys) == 0 {
525 return nil
526 }
527
528 var et errorTracker
529 it := mma.iterator(et.init(mma))
530 meta := NewMultiMetaGetter(pms)
531 err = filterStop(Raw(c).GetMulti(keys, meta, func(pm PropertyMap, err er ror) error {
532 it.next(func(mat *multiArgType, slot reflect.Value) error {
533 if err != nil {
534 return err
535 }
536 return mat.setPM(slot, pm)
537 })
538 return nil
539 }))
540
541 if err == nil {
542 err = et.error()
543 }
544 return maybeSingleError(err, dst)
545 }
546
547 // Put writes objects into the datastore.
548 //
549 // src must be one of:
550 // - *S, where S is a struct
551 // - *P, where *P is a concrete type implementing PropertyLoadSaver
552 // - []S or []*S, where S is a struct
553 // - []P or []*P, where *P is a concrete type implementing PropertyLoadSave r
554 // - []I, where I is some interface type. Each element of the slice must
555 // be non-nil, and its underlying type must be either *S or *P.
556 //
557 // A *Key will be extracted from src via KeyForObj. If
558 // extractedKey.Incomplete() is true, then Put will write the resolved (i.e.
559 // automatic datastore-populated) *Key back to src.
560 //
561 // If an error is encountered, the returned error value will depend on the
562 // input arguments. If one argument is supplied, the result will be the
563 // encountered error type. If multiple arguments are supplied, the result will
564 // be a MultiError whose error index corresponds to the argument in which the
565 // error was encountered.
566 //
567 // If a src argument is a slice, its error type will be a MultiError. Note
568 // that in the scenario where multiple slices are provided, this will return a
569 // MultiError containing a nested MultiError for each slice argument.
570 func Put(c context.Context, src ...interface{}) error {
571 if len(src) == 0 {
572 return nil
573 }
574
575 mma, err := makeMetaMultiArg(src, mmaReadWrite)
576 if err != nil {
577 panic(err)
578 }
579
580 keys, vals, err := mma.getKeysPMs(GetKeyContext(c), false)
581 if err != nil {
582 return maybeSingleError(err, src)
583 }
584 if len(keys) == 0 {
585 return nil
586 }
587
588 i := 0
589 var et errorTracker
590 it := mma.iterator(et.init(mma))
591 err = filterStop(Raw(c).PutMulti(keys, vals, func(key *Key, err error) e rror {
592 it.next(func(mat *multiArgType, slot reflect.Value) error {
593 if err != nil {
594 return err
595 }
596 if key != keys[i] {
597 mat.setKey(slot, key)
598 }
599 return nil
600 })
601
602 i++
603 return nil
604 }))
605
606 if err == nil {
607 err = et.error()
608 }
609 return maybeSingleError(err, src)
610 }
611
612 // Delete removes the supplied entities from the datastore.
613 //
614 // ent must be one of:
615 // - *S, where S is a struct
616 // - *P, where *P is a concrete type implementing PropertyLoadSaver
617 // - []S or []*S, where S is a struct
618 // - []P or []*P, where *P is a concrete type implementing PropertyLoadSave r
619 // - []I, where I is some interface type. Each element of the slice must
620 // be non-nil, and its underlying type must be either *S or *P.
621 // - *Key, to remove a specific key from the datastore.
622 // - []*Key, to remove a slice of keys from the datastore.
623 //
624 // If an error is encountered, the returned error value will depend on the
625 // input arguments. If one argument is supplied, the result will be the
626 // encountered error type. If multiple arguments are supplied, the result will
627 // be a MultiError whose error index corresponds to the argument in which the
628 // error was encountered.
629 //
630 // If an ent argument is a slice, its error type will be a MultiError. Note
631 // that in the scenario where multiple slices are provided, this will return a
632 // MultiError containing a nested MultiError for each slice argument.
633 func Delete(c context.Context, ent ...interface{}) error {
634 if len(ent) == 0 {
635 return nil
636 }
637
638 mma, err := makeMetaMultiArg(ent, mmaKeysOnly)
639 if err != nil {
640 panic(err)
641 }
642
643 keys, _, err := mma.getKeysPMs(GetKeyContext(c), false)
644 if err != nil {
645 return maybeSingleError(err, ent)
646 }
647 if len(keys) == 0 {
648 return nil
649 }
650
651 var et errorTracker
652 it := mma.iterator(et.init(mma))
653 err = filterStop(Raw(c).DeleteMulti(keys, func(err error) error {
654 it.next(func(*multiArgType, reflect.Value) error {
655 return err
656 })
657
658 return nil
659 }))
660 if err == nil {
661 err = et.error()
662 }
663 return maybeSingleError(err, ent)
664 }
665
666 // GetTestable returns the Testable interface for the implementation, or nil if
667 // there is none.
668 func GetTestable(c context.Context) Testable {
669 return Raw(c).GetTestable()
670 }
671
672 // maybeSingleError normalizes the error experience between single- and
673 // multi-element API calls.
674 //
675 // Single-element API calls will return a single error for that element, while
676 // multi-element API calls will return a MultiError, one for each element. This
677 // accepts the slice of elements that is being operated on and determines what
678 // sort of error to return.
679 func maybeSingleError(err error, elems []interface{}) error {
680 if err == nil {
681 return nil
682 }
683 if len(elems) == 1 {
684 return errors.SingleError(err)
685 }
686 return err
687 }
688
689 func filterStop(err error) error {
690 if err == Stop {
691 err = nil
692 }
693 return err
694 }
OLDNEW
« no previous file with comments | « service/datastore/finalized_query.go ('k') | service/datastore/key.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698