OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package memory | 5 package memory |
6 | 6 |
7 import ( | 7 import ( |
8 "bytes" | 8 "bytes" |
| 9 "errors" |
9 "fmt" | 10 "fmt" |
| 11 "strings" |
10 | 12 |
11 ds "github.com/luci/gae/service/datastore" | 13 ds "github.com/luci/gae/service/datastore" |
12 "github.com/luci/gae/service/datastore/serialize" | 14 "github.com/luci/gae/service/datastore/serialize" |
13 "github.com/luci/luci-go/common/cmpbin" | 15 "github.com/luci/luci-go/common/cmpbin" |
14 "github.com/luci/luci-go/common/stringset" | 16 "github.com/luci/luci-go/common/stringset" |
15 ) | 17 ) |
16 | 18 |
17 type queryStrategy interface { | 19 type queryStrategy interface { |
18 // handle applies the strategy to the embedded user callback. | 20 // handle applies the strategy to the embedded user callback. |
19 // - rawData is the slice of encoded Properties from the index row | 21 // - rawData is the slice of encoded Properties from the index row |
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
171 return | 173 return |
172 } | 174 } |
173 } | 175 } |
174 err = executeQuery(fq, aid, ns, isTxn, idx, head, func(_ *ds.Key, _ ds.P
ropertyMap, _ ds.CursorCB) error { | 176 err = executeQuery(fq, aid, ns, isTxn, idx, head, func(_ *ds.Key, _ ds.P
ropertyMap, _ ds.CursorCB) error { |
175 ret++ | 177 ret++ |
176 return nil | 178 return nil |
177 }) | 179 }) |
178 return | 180 return |
179 } | 181 } |
180 | 182 |
| 183 func executeNamespaceQuery(fq *ds.FinalizedQuery, aid string, head *memStore, cb
ds.RawRunCB) error { |
| 184 // these objects have no properties, so any filters on properties cause
an |
| 185 // empty result. |
| 186 if len(fq.EqFilters()) > 0 || len(fq.Project()) > 0 || len(fq.Orders())
> 1 { |
| 187 return nil |
| 188 } |
| 189 if !(fq.IneqFilterProp() == "" || fq.IneqFilterProp() == "__key__") { |
| 190 return nil |
| 191 } |
| 192 colls := head.GetCollectionNames() |
| 193 foundEnts := false |
| 194 limit, hasLimit := fq.Limit() |
| 195 offset, hasOffset := fq.Offset() |
| 196 start, end := fq.Bounds() |
| 197 |
| 198 cursErr := errors.New("cursors not supported for __namespace__ query") |
| 199 cursFn := func() (ds.Cursor, error) { return nil, cursErr } |
| 200 if !(start == nil && end == nil) { |
| 201 return cursErr |
| 202 } |
| 203 for _, c := range colls { |
| 204 if !strings.HasPrefix(c, "ents:") { |
| 205 if foundEnts { |
| 206 break |
| 207 } |
| 208 continue |
| 209 } |
| 210 foundEnts = true |
| 211 if hasOffset && offset > 0 { |
| 212 offset-- |
| 213 continue |
| 214 } |
| 215 if hasLimit { |
| 216 if limit <= 0 { |
| 217 return ds.Stop |
| 218 } |
| 219 limit-- |
| 220 } |
| 221 k := (*ds.Key)(nil) |
| 222 ns := c[len("ents:"):] |
| 223 if ns == "" { |
| 224 // Datastore uses an id of 1 to indicate the default nam
espace in its |
| 225 // metadata API. |
| 226 k = ds.MakeKey(aid, "", "__namespace__", 1) |
| 227 } else { |
| 228 k = ds.MakeKey(aid, "", "__namespace__", ns) |
| 229 } |
| 230 if err := cb(k, nil, cursFn); err != nil { |
| 231 return err |
| 232 } |
| 233 } |
| 234 return nil |
| 235 } |
| 236 |
181 func executeQuery(fq *ds.FinalizedQuery, aid, ns string, isTxn bool, idx, head *
memStore, cb ds.RawRunCB) error { | 237 func executeQuery(fq *ds.FinalizedQuery, aid, ns string, isTxn bool, idx, head *
memStore, cb ds.RawRunCB) error { |
182 rq, err := reduce(fq, aid, ns, isTxn) | 238 rq, err := reduce(fq, aid, ns, isTxn) |
183 if err == ds.ErrNullQuery { | 239 if err == ds.ErrNullQuery { |
184 return nil | 240 return nil |
185 } | 241 } |
186 if err != nil { | 242 if err != nil { |
187 return err | 243 return err |
188 } | 244 } |
189 | 245 |
| 246 if rq.kind == "__namespace__" { |
| 247 return executeNamespaceQuery(fq, aid, head, cb) |
| 248 } |
| 249 |
190 idxs, err := getIndexes(rq, idx) | 250 idxs, err := getIndexes(rq, idx) |
191 if err == ds.ErrNullQuery { | 251 if err == ds.ErrNullQuery { |
192 return nil | 252 return nil |
193 } | 253 } |
194 if err != nil { | 254 if err != nil { |
195 return err | 255 return err |
196 } | 256 } |
197 | 257 |
198 strategy := pickQueryStrategy(fq, rq, cb, head) | 258 strategy := pickQueryStrategy(fq, rq, cb, head) |
199 if strategy == nil { | 259 if strategy == nil { |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
242 keyProp := decodedProps[len(decodedProps)-1] | 302 keyProp := decodedProps[len(decodedProps)-1] |
243 if keyProp.Type() != ds.PTKey { | 303 if keyProp.Type() != ds.PTKey { |
244 impossible(fmt.Errorf("decoded index row doesn't end wit
h a Key: %#v", keyProp)) | 304 impossible(fmt.Errorf("decoded index row doesn't end wit
h a Key: %#v", keyProp)) |
245 } | 305 } |
246 | 306 |
247 return strategy.handle( | 307 return strategy.handle( |
248 rawData, decodedProps, keyProp.Value().(*ds.Key), | 308 rawData, decodedProps, keyProp.Value().(*ds.Key), |
249 getCursorFn(suffix)) | 309 getCursorFn(suffix)) |
250 }) | 310 }) |
251 } | 311 } |
OLD | NEW |