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 "errors" | 9 "errors" |
9 "fmt" | 10 "fmt" |
10 "math" | 11 "math" |
11 "strings" | 12 "strings" |
12 | 13 |
13 ds "github.com/luci/gae/service/datastore" | 14 ds "github.com/luci/gae/service/datastore" |
14 "github.com/luci/gkvlite" | |
15 ) | 15 ) |
16 | 16 |
| 17 // MaxQueryComponents was lifted from a hard-coded constant in dev_appserver. |
| 18 // No idea if it's a real limit or just a convenience in the current dev |
| 19 // appserver implementation. |
| 20 const MaxQueryComponents = 100 |
| 21 |
| 22 var errQueryDone = errors.New("query is done") |
| 23 |
17 type queryOp int | 24 type queryOp int |
18 | 25 |
19 const ( | 26 const ( |
20 qInvalid queryOp = iota | 27 qInvalid queryOp = iota |
21 qEqual | 28 qEqual |
22 qLessThan | 29 qLessThan |
23 qLessEq | 30 qLessEq |
24 qGreaterEq | 31 qGreaterEq |
25 qGreaterThan | 32 qGreaterThan |
26 ) | 33 ) |
27 | 34 |
28 func (o queryOp) isEQOp() bool { | |
29 return o == qEqual | |
30 } | |
31 | |
32 func (o queryOp) isINEQOp() bool { | |
33 return o >= qLessThan && o <= qGreaterThan | |
34 } | |
35 | |
36 var queryOpMap = map[string]queryOp{ | 35 var queryOpMap = map[string]queryOp{ |
37 "=": qEqual, | 36 "=": qEqual, |
38 "<": qLessThan, | 37 "<": qLessThan, |
39 "<=": qLessEq, | 38 "<=": qLessEq, |
40 ">=": qGreaterEq, | 39 ">=": qGreaterEq, |
41 ">": qGreaterThan, | 40 ">": qGreaterThan, |
42 } | 41 } |
43 | 42 |
44 type queryFilter struct { | 43 type queryFilter struct { |
45 prop string | 44 prop string |
46 op queryOp | 45 op queryOp |
47 value interface{} | 46 value interface{} |
48 } | 47 } |
49 | 48 |
50 func parseFilter(f string, v interface{}) (ret queryFilter, err error) { | 49 func parseFilter(f string) (prop string, op queryOp, err error) { |
51 toks := strings.SplitN(strings.TrimSpace(f), " ", 2) | 50 toks := strings.SplitN(strings.TrimSpace(f), " ", 2) |
52 if len(toks) != 2 { | 51 if len(toks) != 2 { |
53 err = errors.New("datastore: invalid filter: " + f) | 52 err = errors.New("datastore: invalid filter: " + f) |
54 } else { | 53 } else { |
55 » » op := queryOpMap[toks[1]] | 54 » » op = queryOpMap[toks[1]] |
56 if op == qInvalid { | 55 if op == qInvalid { |
57 err = fmt.Errorf("datastore: invalid operator %q in filt
er %q", toks[1], f) | 56 err = fmt.Errorf("datastore: invalid operator %q in filt
er %q", toks[1], f) |
58 } else { | 57 } else { |
59 » » » ret.prop = toks[0] | 58 » » » prop = toks[0] |
60 » » » ret.op = op | |
61 » » » ret.value = v | |
62 } | 59 } |
63 } | 60 } |
64 return | 61 return |
65 } | 62 } |
66 | 63 |
67 type queryCursor string | 64 type queryCursor string |
68 | 65 |
69 func (q queryCursor) String() string { return string(q) } | 66 func (q queryCursor) String() string { return string(q) } |
70 func (q queryCursor) Valid() bool { return q != "" } | 67 func (q queryCursor) Valid() bool { return q != "" } |
71 | 68 |
| 69 type queryIneqFilter struct { |
| 70 prop string |
| 71 |
| 72 low *string |
| 73 high *string |
| 74 } |
| 75 |
| 76 func increment(bstr string, positive bool) string { |
| 77 lastIdx := len(bstr) - 1 |
| 78 last := bstr[lastIdx] |
| 79 if positive { |
| 80 if last == 0xFF { |
| 81 return bstr + "\x00" |
| 82 } |
| 83 return bstr[:lastIdx-1] + string(last+1) |
| 84 } else { |
| 85 if last == 0 { |
| 86 return bstr[:lastIdx-1] |
| 87 } |
| 88 return bstr[:lastIdx-1] + string(last-1) |
| 89 } |
| 90 } |
| 91 |
| 92 // constrain 'folds' a new inequality into the current inequality filter. |
| 93 // |
| 94 // It will bump the high bound down, or the low bound up, assuming the incoming |
| 95 // constraint does so. |
| 96 // |
| 97 // It returns true iff the filter is overconstrained (i.e. low > high) |
| 98 func (q *queryIneqFilter) constrain(op queryOp, val string) bool { |
| 99 switch op { |
| 100 case qLessThan: |
| 101 val = increment(val, true) |
| 102 fallthrough |
| 103 case qLessEq: |
| 104 // adjust upper bound downwards |
| 105 if q.high == nil || *q.high > val { |
| 106 q.high = &val |
| 107 } |
| 108 |
| 109 case qGreaterThan: |
| 110 val = increment(val, false) |
| 111 fallthrough |
| 112 case qGreaterEq: |
| 113 // adjust lower bound upwards |
| 114 if q.low == nil || *q.low < val { |
| 115 q.low = &val |
| 116 } |
| 117 |
| 118 default: |
| 119 panic(fmt.Errorf("constrain cannot handle filter op %d", op)) |
| 120 } |
| 121 |
| 122 if q.low != nil && q.high != nil { |
| 123 return *q.low > *q.high |
| 124 } |
| 125 return false |
| 126 } |
| 127 |
72 type queryImpl struct { | 128 type queryImpl struct { |
73 ns string | 129 ns string |
74 | 130 |
75 kind string | 131 kind string |
76 ancestor ds.Key | 132 ancestor ds.Key |
77 » filter []queryFilter | 133 |
78 » order []ds.IndexColumn | 134 » // prop -> encoded values |
79 » project []string | 135 » eqFilters map[string]map[string]struct{} |
| 136 » ineqFilter queryIneqFilter |
| 137 » order []ds.IndexColumn |
| 138 » project map[string]struct{} |
80 | 139 |
81 distinct bool | 140 distinct bool |
82 eventualConsistency bool | 141 eventualConsistency bool |
83 keysOnly bool | 142 keysOnly bool |
84 limit int32 | 143 limit int32 |
85 offset int32 | 144 offset int32 |
86 | 145 |
87 start queryCursor | 146 start queryCursor |
88 end queryCursor | 147 end queryCursor |
89 | 148 |
90 err error | 149 err error |
91 } | 150 } |
92 | 151 |
93 var _ ds.Query = (*queryImpl)(nil) | 152 var _ ds.Query = (*queryImpl)(nil) |
94 | 153 |
95 func (q *queryImpl) normalize() (ret *queryImpl) { | 154 func (q *queryImpl) valid(ns string, isTxn bool) (done bool, err error) { |
96 » // ported from GAE SDK datastore_index.py;Normalize() | 155 » if q.err == errQueryDone { |
97 » ret = q.clone() | 156 » » done = true |
98 | 157 » } else if q.err != nil { |
99 » bs := newMemStore() | 158 » » err = q.err |
100 | 159 » } else if ns != q.ns { |
101 » eqProperties := bs.MakePrivateCollection(nil) | 160 » » err = errors.New( |
102 | |
103 » ineqProperties := bs.MakePrivateCollection(nil) | |
104 | |
105 » for _, f := range ret.filter { | |
106 » » // if we supported the IN operator, we would check to see if the
re were | |
107 » » // multiple value operands here, but the go SDK doesn't support
this. | |
108 » » if f.op.isEQOp() { | |
109 » » » eqProperties.Set([]byte(f.prop), []byte{}) | |
110 » » } else if f.op.isINEQOp() { | |
111 » » » ineqProperties.Set([]byte(f.prop), []byte{}) | |
112 » » } | |
113 » } | |
114 | |
115 » ineqProperties.VisitItemsAscend(nil, false, func(i *gkvlite.Item) bool { | |
116 » » eqProperties.Delete(i.Key) | |
117 » » return true | |
118 » }) | |
119 | |
120 » removeSet := bs.MakePrivateCollection(nil) | |
121 » eqProperties.VisitItemsAscend(nil, false, func(i *gkvlite.Item) bool { | |
122 » » removeSet.Set(i.Key, []byte{}) | |
123 » » return true | |
124 » }) | |
125 | |
126 » newOrders := []ds.IndexColumn{} | |
127 » for _, o := range ret.order { | |
128 » » if removeSet.Get([]byte(o.Property)) == nil { | |
129 » » » removeSet.Set([]byte(o.Property), []byte{}) | |
130 » » » newOrders = append(newOrders, o) | |
131 » » } | |
132 » } | |
133 » ret.order = newOrders | |
134 | |
135 » // need to fix ret.filters if we ever support the EXISTS operator and/or | |
136 » // projections. | |
137 » // | |
138 » // newFilters = [] | |
139 » // for f in ret.filters: | |
140 » // if f.op != qExists: | |
141 » // newFilters = append(newFilters, f) | |
142 » // if !removeSet.Has(f.prop): | |
143 » // removeSet.InsertNoReplace(f.prop) | |
144 » // newFilters = append(newFilters, f) | |
145 » // | |
146 » // so ret.filters == newFilters becuase none of ret.filters has op == qE
xists | |
147 » // | |
148 » // then: | |
149 » // | |
150 » // for prop in ret.project: | |
151 » // if !removeSet.Has(prop): | |
152 » // removeSet.InsertNoReplace(prop) | |
153 » // ... make new EXISTS filters, add them to newFilters ... | |
154 » // ret.filters = newFilters | |
155 » // | |
156 » // However, since we don't support projection queries, this is moot. | |
157 | |
158 » if eqProperties.Get([]byte("__key__")) != nil { | |
159 » » ret.order = []ds.IndexColumn{} | |
160 » } | |
161 | |
162 » newOrders = []ds.IndexColumn{} | |
163 » for _, o := range ret.order { | |
164 » » if o.Property == "__key__" { | |
165 » » » newOrders = append(newOrders, o) | |
166 » » » break | |
167 » » } | |
168 » » newOrders = append(newOrders, o) | |
169 » } | |
170 » ret.order = newOrders | |
171 | |
172 » return | |
173 } | |
174 | |
175 func (q *queryImpl) checkCorrectness(ns string, isTxn bool) (ret *queryImpl) { | |
176 » // ported from GAE SDK datastore_stub_util.py;CheckQuery() | |
177 » ret = q.clone() | |
178 | |
179 » if ns != ret.ns { | |
180 » » ret.err = errors.New( | |
181 "gae/memory: Namespace mismatched. Query and Datastore d
on't agree " + | 161 "gae/memory: Namespace mismatched. Query and Datastore d
on't agree " + |
182 "on the current namespace") | 162 "on the current namespace") |
183 » » return | 163 » } else if isTxn && q.ancestor == nil { |
184 » } | 164 » » err = errors.New( |
185 | |
186 » if ret.err != nil { | |
187 » » return | |
188 » } | |
189 | |
190 » // if projection && keys_only: | |
191 » // "projection and keys_only cannot both be set" | |
192 | |
193 » // if projection props match /^__.*__$/: | |
194 » // "projections are not supported for the property: %(prop)s" | |
195 | |
196 » if isTxn && ret.ancestor == nil { | |
197 » » ret.err = errors.New( | |
198 "gae/memory: Only ancestor queries are allowed inside tr
ansactions") | 165 "gae/memory: Only ancestor queries are allowed inside tr
ansactions") |
199 » » return | 166 » } else if q.numComponents() > MaxQueryComponents { |
200 » } | 167 » » err = fmt.Errorf( |
201 | 168 » » » "gae/memory: query is too large. may not have more than
"+ |
202 » numComponents := len(ret.filter) + len(ret.order) | 169 » » » » "%d filters + sort orders + ancestor total: had
%d", |
203 » if ret.ancestor != nil { | 170 » » » MaxQueryComponents, q.numComponents()) |
204 » » numComponents++ | 171 » } else if len(q.project) == 0 && q.distinct { |
205 » } | 172 » » // This must be delayed, because q.Distinct().Project("foo") is
a valid |
206 » if numComponents > 100 { | 173 » » // construction. If we checked this in Distinct, it could be too
early, and |
207 » » ret.err = errors.New( | 174 » » // checking it in Project doesn't matter. |
208 » » » "gae/memory: query is too large. may not have more than
" + | 175 » » err = errors.New( |
209 » » » » "100 filters + sort orders ancestor total") | 176 » » » "gae/memory: Distinct() only makes sense on projection q
ueries.") |
210 » } | |
211 | |
212 » // if ret.ancestor.appid() != current appid | |
213 » // "query app is x but ancestor app is x" | |
214 » // if ret.ancestor.namespace() != current namespace | |
215 » // "query namespace is x but ancestor namespace is x" | |
216 | |
217 » // if not all(g in orders for g in group_by) | |
218 » // "items in the group by clause must be specified first in the orderin
g" | |
219 | |
220 » ineqPropName := "" | |
221 » for _, f := range ret.filter { | |
222 » » if f.prop == "__key__" { | |
223 » » » k, ok := f.value.(ds.Key) | |
224 » » » if !ok { | |
225 » » » » ret.err = errors.New( | |
226 » » » » » "gae/memory: __key__ filter value must b
e a Key") | |
227 » » » » return | |
228 » » » } | |
229 » » » if !ds.KeyValid(k, false, globalAppID, q.ns) { | |
230 » » » » // See the comment in queryImpl.Ancestor; basica
lly this check | |
231 » » » » // never happens in the real env because the SDK
silently swallows | |
232 » » » » // this condition :/ | |
233 » » » » ret.err = ds.ErrInvalidKey | |
234 » » » » return | |
235 » » » } | |
236 » » » if k.Namespace() != ns { | |
237 » » » » ret.err = fmt.Errorf("bad namespace: %q (expecte
d %q)", k.Namespace(), ns) | |
238 » » » » return | |
239 » » » } | |
240 » » » // __key__ filter app is X but query app is X | |
241 » » » // __key__ filter namespace is X but query namespace is
X | |
242 » » } | |
243 » » // if f.op == qEqual and f.prop in ret.project_fields | |
244 » » // "cannot use projection on a proprety with an equality filte
r" | |
245 | |
246 » » if f.op.isINEQOp() { | |
247 » » » if ineqPropName == "" { | |
248 » » » » ineqPropName = f.prop | |
249 » » » } else if f.prop != ineqPropName { | |
250 » » » » ret.err = fmt.Errorf( | |
251 » » » » » "gae/memory: Only one inequality filter
per query is supported. "+ | |
252 » » » » » » "Encountered both %s and %s", in
eqPropName, f.prop) | |
253 » » » » return | |
254 » » » } | |
255 » » } | |
256 » } | |
257 | |
258 » // if ineqPropName != "" && len(group_by) > 0 && len(orders) ==0 | |
259 » // "Inequality filter on X must also be a group by property "+ | |
260 » // "when group by properties are set." | |
261 | |
262 » if ineqPropName != "" && len(ret.order) != 0 { | |
263 » » if ret.order[0].Property != ineqPropName { | |
264 » » » ret.err = fmt.Errorf( | |
265 » » » » "gae/memory: The first sort property must be the
same as the property "+ | |
266 » » » » » "to which the inequality filter is appli
ed. In your query "+ | |
267 » » » » » "the first sort property is %s but the i
nequality filter "+ | |
268 » » » » » "is on %s", ret.order[0].Property, ineqP
ropName) | |
269 » » » return | |
270 » » } | |
271 » } | |
272 | |
273 » if ret.kind == "" { | |
274 » » for _, f := range ret.filter { | |
275 » » » if f.prop != "__key__" { | |
276 » » » » ret.err = errors.New( | |
277 » » » » » "gae/memory: kind is required for non-__
key__ filters") | |
278 » » » » return | |
279 » » » } | |
280 » » } | |
281 » » for _, o := range ret.order { | |
282 » » » if o.Property != "__key__" || o.Direction != ds.ASCENDIN
G { | |
283 » » » » ret.err = errors.New( | |
284 » » » » » "gae/memory: kind is required for all or
ders except __key__ ascending") | |
285 » » » » return | |
286 » » » } | |
287 » » } | |
288 } | 177 } |
289 return | 178 return |
290 } | 179 } |
291 | 180 |
| 181 func (q *queryImpl) numComponents() int { |
| 182 numComponents := len(q.order) |
| 183 if q.ineqFilter.prop != "" { |
| 184 if q.ineqFilter.low != nil { |
| 185 numComponents++ |
| 186 } |
| 187 if q.ineqFilter.high != nil { |
| 188 numComponents++ |
| 189 } |
| 190 } |
| 191 for _, v := range q.eqFilters { |
| 192 numComponents += len(v) |
| 193 } |
| 194 if q.ancestor != nil { |
| 195 numComponents++ |
| 196 } |
| 197 return numComponents |
| 198 } |
| 199 |
292 func (q *queryImpl) calculateIndex() *ds.IndexDefinition { | 200 func (q *queryImpl) calculateIndex() *ds.IndexDefinition { |
293 // as a nod to simplicity in this code, we'll require that a single inde
x | 201 // as a nod to simplicity in this code, we'll require that a single inde
x |
294 // is able to service the entire query. E.g. no zigzag merge joins or | 202 // is able to service the entire query. E.g. no zigzag merge joins or |
295 // multiqueries. This will mean that the user will need to rely on | 203 // multiqueries. This will mean that the user will need to rely on |
296 // dev_appserver to tell them what indicies they need for real, and for
thier | 204 // dev_appserver to tell them what indicies they need for real, and for
thier |
297 // tests they'll need to specify the missing composite indices manually. | 205 // tests they'll need to specify the missing composite indices manually. |
298 // | 206 // |
299 // This COULD lead to an exploding indicies problem, but we can fix that
when | 207 // This COULD lead to an exploding indicies problem, but we can fix that
when |
300 // we get to it. | 208 // we get to it. |
301 | 209 |
302 //sortOrders := []qSortBy{} | 210 //sortOrders := []qSortBy{} |
303 | 211 |
304 return nil | 212 return nil |
305 } | 213 } |
306 | 214 |
307 func (q *queryImpl) clone() *queryImpl { | 215 // checkMutateClone sees if the query has an error. If not, it clones the query, |
308 » ret := *q | 216 // and assigns the output of `check` to the query error slot. If check returns |
309 » ret.filter = append([]queryFilter(nil), q.filter...) | 217 // nil, it calls `mutate` on the cloned query. The (possibly new) query is then |
310 » ret.order = append([]ds.IndexColumn(nil), q.order...) | 218 // returned. |
311 » ret.project = append([]string(nil), q.project...) | 219 func (q *queryImpl) checkMutateClone(check func() error, mutate func(*queryImpl)
) *queryImpl { |
312 » return &ret | 220 » if q.err != nil { |
| 221 » » return q |
| 222 » } |
| 223 » nq := *q |
| 224 » nq.eqFilters = make(map[string]map[string]struct{}, len(q.eqFilters)) |
| 225 » for prop, vals := range q.eqFilters { |
| 226 » » nq.eqFilters[prop] = make(map[string]struct{}, len(vals)) |
| 227 » » for v := range vals { |
| 228 » » » nq.eqFilters[prop][v] = struct{}{} |
| 229 » » } |
| 230 » } |
| 231 » nq.order = make([]ds.IndexColumn, len(q.order)) |
| 232 » copy(nq.order, q.order) |
| 233 » nq.project = make(map[string]struct{}, len(q.project)) |
| 234 » for f := range q.project { |
| 235 » » nq.project[f] = struct{}{} |
| 236 » } |
| 237 » if check != nil { |
| 238 » » nq.err = check() |
| 239 » } |
| 240 » if nq.err == nil { |
| 241 » » mutate(&nq) |
| 242 » } |
| 243 » return &nq |
313 } | 244 } |
314 | 245 |
315 func (q *queryImpl) Ancestor(k ds.Key) ds.Query { | 246 func (q *queryImpl) Ancestor(k ds.Key) ds.Query { |
316 » q = q.clone() | 247 » return q.checkMutateClone( |
317 » q.ancestor = k | 248 » » func() error { |
318 » if k == nil { | 249 » » » if k == nil { |
319 » » // SDK has an explicit nil-check | 250 » » » » // SDK has an explicit nil-check |
320 » » q.err = errors.New("datastore: nil query ancestor") | 251 » » » » return errors.New("datastore: nil query ancestor
") |
321 » } else if !ds.KeyValid(k, false, globalAppID, q.ns) { | 252 » » » } |
322 » » // technically the SDK implementation does a Weird Thing (tm) if
both the | 253 » » » if !ds.KeyValid(k, false, globalAppID, q.ns) { |
323 » » // stringID and intID are set on a key; it only serializes the s
tringID in | 254 » » » » // technically the SDK implementation does a Wei
rd Thing (tm) if both the |
324 » » // the proto. This means that if you set the Ancestor to an inva
lid key, | 255 » » » » // stringID and intID are set on a key; it only
serializes the stringID in |
325 » » // you'll never actually hear about it. Instead of doing that in
sanity, we | 256 » » » » // the proto. This means that if you set the Anc
estor to an invalid key, |
326 » » // just swap to an error here. | 257 » » » » // you'll never actually hear about it. Instead
of doing that insanity, we |
327 » » q.err = ds.ErrInvalidKey | 258 » » » » // just swap to an error here. |
328 » } else if k.Namespace() != q.ns { | 259 » » » » return ds.ErrInvalidKey |
329 » » q.err = fmt.Errorf("bad namespace: %q (expected %q)", k.Namespac
e(), q.ns) | 260 » » » } |
330 » } | 261 » » » if k.Namespace() != q.ns { |
331 » return q | 262 » » » » return fmt.Errorf("bad namespace: %q (expected %
q)", k.Namespace(), q.ns) |
| 263 » » » } |
| 264 » » » if q.ancestor != nil { |
| 265 » » » » return errors.New("cannot have more than one anc
estor") |
| 266 » » » } |
| 267 » » » return nil |
| 268 » » }, |
| 269 » » func(q *queryImpl) { |
| 270 » » » q.ancestor = k |
| 271 » » }) |
332 } | 272 } |
333 | 273 |
334 func (q *queryImpl) Distinct() ds.Query { | 274 func (q *queryImpl) Distinct() ds.Query { |
335 » q = q.clone() | 275 » return q.checkMutateClone(nil, func(q *queryImpl) { |
336 » q.distinct = true | 276 » » q.distinct = true |
337 » return q | 277 » }) |
338 } | 278 } |
339 | 279 |
340 func (q *queryImpl) Filter(fStr string, val interface{}) ds.Query { | 280 func (q *queryImpl) Filter(fStr string, val interface{}) ds.Query { |
341 » q = q.clone() | 281 » prop := "" |
342 » f, err := parseFilter(fStr, val) | 282 » op := qInvalid |
343 » if err != nil { | 283 » binVal := "" |
344 » » q.err = err | 284 » return q.checkMutateClone( |
345 » » return q | 285 » » func() error { |
346 » } | 286 » » » var err error |
347 » q.filter = append(q.filter, f) | 287 » » » prop, op, err = parseFilter(fStr) |
348 » return q | 288 » » » if err != nil { |
| 289 » » » » return err |
| 290 » » » } |
| 291 |
| 292 » » » if q.kind == "" && prop != "__key__" { |
| 293 » » » » // https://cloud.google.com/appengine/docs/go/da
tastore/queries#Go_Kindless_queries |
| 294 » » » » return fmt.Errorf( |
| 295 » » » » » "kindless queries can only filter on __k
ey__, got %q", fStr) |
| 296 » » » } |
| 297 |
| 298 » » » p := ds.Property{} |
| 299 » » » err = p.SetValue(val, ds.NoIndex) |
| 300 » » » if err != nil { |
| 301 » » » » return err |
| 302 » » » } |
| 303 |
| 304 » » » if p.Type() == ds.PTKey { |
| 305 » » » » if !ds.KeyValid(p.Value().(ds.Key), false, globa
lAppID, q.ns) { |
| 306 » » » » » return ds.ErrInvalidKey |
| 307 » » » » } |
| 308 » » » } |
| 309 |
| 310 » » » if prop == "__key__" { |
| 311 » » » » if op == qEqual { |
| 312 » » » » » return fmt.Errorf( |
| 313 » » » » » » "query equality filter on __key_
_ is silly: %q", fStr) |
| 314 » » » » } |
| 315 » » » » if p.Type() != ds.PTKey { |
| 316 » » » » » return fmt.Errorf("__key__ filter value
is not a key: %T", val) |
| 317 » » » » } |
| 318 » » » } |
| 319 |
| 320 » » » if op != qEqual { |
| 321 » » » » if q.ineqFilter.prop != "" && q.ineqFilter.prop
!= prop { |
| 322 » » » » » return fmt.Errorf( |
| 323 » » » » » » "inequality filters on multiple
properties: %q and %q", |
| 324 » » » » » » q.ineqFilter.prop, prop) |
| 325 » » » » } |
| 326 » » » » if len(q.order) > 0 && q.order[0].Property != pr
op { |
| 327 » » » » » return fmt.Errorf( |
| 328 » » » » » » "first sort order must match ine
quality filter: %q v %q", |
| 329 » » » » » » q.order[0].Property, prop) |
| 330 » » » » } |
| 331 » » » } else if _, ok := q.project[prop]; ok { |
| 332 » » » » return fmt.Errorf( |
| 333 » » » » » "cannot project on field which is used i
n an equality filter: %q", |
| 334 » » » » » prop) |
| 335 » » » } |
| 336 |
| 337 » » » buf := &bytes.Buffer{} |
| 338 » » » p.Write(buf, ds.WithoutContext) |
| 339 » » » binVal = buf.String() |
| 340 » » » return nil |
| 341 » » }, |
| 342 » » func(q *queryImpl) { |
| 343 » » » if op == qEqual { |
| 344 » » » » // add it to eq filters |
| 345 » » » » if _, ok := q.eqFilters[prop]; !ok { |
| 346 » » » » » q.eqFilters[prop] = map[string]struct{}{
binVal: {}} |
| 347 » » » » } else { |
| 348 » » » » » q.eqFilters[prop][binVal] = struct{}{} |
| 349 » » » » } |
| 350 |
| 351 » » » » // remove it from sort orders. |
| 352 » » » » // https://cloud.google.com/appengine/docs/go/da
tastore/queries#sort_orders_are_ignored_on_properties_with_equality_filters |
| 353 » » » » toRm := -1 |
| 354 » » » » for i, o := range q.order { |
| 355 » » » » » if o.Property == prop { |
| 356 » » » » » » toRm = i |
| 357 » » » » » » break |
| 358 » » » » » } |
| 359 » » » » } |
| 360 » » » » if toRm >= 0 { |
| 361 » » » » » q.order = append(q.order[:toRm], q.order
[toRm+1:]...) |
| 362 » » » » } |
| 363 » » » } else { |
| 364 » » » » q.ineqFilter.prop = prop |
| 365 » » » » if q.ineqFilter.constrain(op, binVal) { |
| 366 » » » » » q.err = errQueryDone |
| 367 » » » » } |
| 368 » » » } |
| 369 » » }) |
349 } | 370 } |
350 | 371 |
351 func (q *queryImpl) Order(prop string) ds.Query { | 372 func (q *queryImpl) Order(prop string) ds.Query { |
352 » q = q.clone() | 373 » col := ds.IndexColumn{} |
353 » prop = strings.TrimSpace(prop) | 374 » return q.checkMutateClone( |
354 » o := ds.IndexColumn{Property: prop} | 375 » » func() error { |
355 » if strings.HasPrefix(prop, "-") { | 376 » » » // check that first order == first inequality. |
356 » » o.Direction = ds.DESCENDING | 377 » » » // if order is an equality already, ignore it |
357 » » o.Property = strings.TrimSpace(prop[1:]) | 378 » » » col.Property = strings.TrimSpace(prop) |
358 » } else if strings.HasPrefix(prop, "+") { | 379 » » » if strings.HasPrefix(prop, "-") { |
359 » » q.err = fmt.Errorf("datastore: invalid order: %q", prop) | 380 » » » » col.Direction = ds.DESCENDING |
360 » » return q | 381 » » » » col.Property = strings.TrimSpace(prop[1:]) |
361 » } | 382 » » » } else if strings.HasPrefix(prop, "+") { |
362 » if len(o.Property) == 0 { | 383 » » » » return fmt.Errorf("datastore: invalid order: %q"
, prop) |
363 » » q.err = errors.New("datastore: empty order") | 384 » » » } |
364 » » return q | 385 » » » if len(col.Property) == 0 { |
365 » } | 386 » » » » return errors.New("datastore: empty order") |
366 » q.order = append(q.order, o) | 387 » » » } |
367 » return q | 388 » » » if q.ineqFilter.prop != "" && q.ineqFilter.prop != col.P
roperty { |
| 389 » » » » return fmt.Errorf( |
| 390 » » » » » "first sort order must match inequality
filter: %q v %q", |
| 391 » » » » » prop, q.ineqFilter.prop) |
| 392 » » » } |
| 393 » » » if q.kind == "" && (col.Property != "__key__" || col.Dir
ection != ds.ASCENDING) { |
| 394 » » » » return fmt.Errorf("invalid order for kindless qu
ery: %#v", col) |
| 395 » » » } |
| 396 » » » return nil |
| 397 » » }, |
| 398 » » func(q *queryImpl) { |
| 399 » » » if _, ok := q.eqFilters[col.Property]; ok { |
| 400 » » » » // skip it if it's an equality filter |
| 401 » » » » // https://cloud.google.com/appengine/docs/go/da
tastore/queries#sort_orders_are_ignored_on_properties_with_equality_filters |
| 402 » » » » return |
| 403 » » » } |
| 404 » » » for _, order := range q.order { |
| 405 » » » » if order.Property == col.Property { |
| 406 » » » » » // can't sort by the same order twice |
| 407 » » » » » return |
| 408 » » » » } |
| 409 » » » } |
| 410 » » » if col.Property == "__key__" { |
| 411 » » » » // __key__ order dominates all other orders |
| 412 » » » » q.order = []ds.IndexColumn{col} |
| 413 » » » } else { |
| 414 » » » » q.order = append(q.order, col) |
| 415 » » » } |
| 416 » » }) |
368 } | 417 } |
369 | 418 |
370 func (q *queryImpl) Project(fieldName ...string) ds.Query { | 419 func (q *queryImpl) Project(fieldName ...string) ds.Query { |
371 » q = q.clone() | 420 » return q.checkMutateClone( |
372 » q.project = append(q.project, fieldName...) | 421 » » func() error { |
373 » return q | 422 » » » if q.keysOnly { |
| 423 » » » » return errors.New("cannot project a keysOnly que
ry") |
| 424 » » » } |
| 425 » » » for _, f := range fieldName { |
| 426 » » » » if f == "" { |
| 427 » » » » » return errors.New("cannot project on an
empty field name") |
| 428 » » » » } |
| 429 » » » » if strings.HasPrefix(f, "__") && strings.HasSuff
ix(f, "__") { |
| 430 » » » » » return fmt.Errorf("cannot project on %q"
, f) |
| 431 » » » » } |
| 432 » » » » if _, ok := q.eqFilters[f]; ok { |
| 433 » » » » » return fmt.Errorf( |
| 434 » » » » » » "cannot project on field which i
s used in an equality filter: %q", f) |
| 435 » » » » } |
| 436 » » » } |
| 437 » » » return nil |
| 438 » » }, |
| 439 » » func(q *queryImpl) { |
| 440 » » » for _, f := range fieldName { |
| 441 » » » » q.project[f] = struct{}{} |
| 442 » » » } |
| 443 » » }) |
374 } | 444 } |
375 | 445 |
376 func (q *queryImpl) KeysOnly() ds.Query { | 446 func (q *queryImpl) KeysOnly() ds.Query { |
377 » q = q.clone() | 447 » return q.checkMutateClone( |
378 » q.keysOnly = true | 448 » » func() error { |
379 » return q | 449 » » » if len(q.project) != 0 { |
| 450 » » » » return errors.New("cannot project a keysOnly que
ry") |
| 451 » » » } |
| 452 » » » return nil |
| 453 » » }, |
| 454 » » func(q *queryImpl) { |
| 455 » » » q.keysOnly = true |
| 456 » » }) |
380 } | 457 } |
381 | 458 |
382 func (q *queryImpl) Limit(limit int) ds.Query { | 459 func (q *queryImpl) Limit(limit int) ds.Query { |
383 » q = q.clone() | 460 » return q.checkMutateClone( |
384 » if limit < math.MinInt32 || limit > math.MaxInt32 { | 461 » » func() error { |
385 » » q.err = errors.New("datastore: query limit overflow") | 462 » » » if limit < math.MinInt32 || limit > math.MaxInt32 { |
386 » » return q | 463 » » » » return errors.New("datastore: query limit overfl
ow") |
387 » } | 464 » » » } |
388 » q.limit = int32(limit) | 465 » » » return nil |
389 » return q | 466 » » }, |
| 467 » » func(q *queryImpl) { |
| 468 » » » q.limit = int32(limit) |
| 469 » » }) |
390 } | 470 } |
391 | 471 |
392 func (q *queryImpl) Offset(offset int) ds.Query { | 472 func (q *queryImpl) Offset(offset int) ds.Query { |
393 » q = q.clone() | 473 » return q.checkMutateClone( |
394 » if offset < 0 { | 474 » » func() error { |
395 » » q.err = errors.New("datastore: negative query offset") | 475 » » » if offset < 0 { |
396 » » return q | 476 » » » » return errors.New("datastore: negative query off
set") |
397 » } | 477 » » » } |
398 » if offset > math.MaxInt32 { | 478 » » » if offset > math.MaxInt32 { |
399 » » q.err = errors.New("datastore: query offset overflow") | 479 » » » » return errors.New("datastore: query offset overf
low") |
400 » » return q | 480 » » » } |
401 » } | 481 » » » return nil |
402 » q.offset = int32(offset) | 482 » » }, |
403 » return q | 483 » » func(q *queryImpl) { |
| 484 » » » q.offset = int32(offset) |
| 485 » » }) |
404 } | 486 } |
405 | 487 |
406 func (q *queryImpl) Start(c ds.Cursor) ds.Query { | 488 func (q *queryImpl) Start(c ds.Cursor) ds.Query { |
407 » q = q.clone() | 489 » curs := queryCursor("") |
408 » curs := c.(queryCursor) | 490 » return q.checkMutateClone( |
409 » if !curs.Valid() { | 491 » » func() error { |
410 » » q.err = errors.New("datastore: invalid cursor") | 492 » » » ok := false |
411 » » return q | 493 » » » if curs, ok = c.(queryCursor); !ok { |
412 » } | 494 » » » » return fmt.Errorf("start cursor is unknown type:
%T", c) |
413 » q.start = curs | 495 » » » } |
414 » return q | 496 » » » if !curs.Valid() { |
| 497 » » » » return errors.New("datastore: invalid cursor") |
| 498 » » » } |
| 499 » » » return nil |
| 500 » » }, |
| 501 » » func(q *queryImpl) { |
| 502 » » » q.start = curs |
| 503 » » }) |
415 } | 504 } |
416 | 505 |
417 func (q *queryImpl) End(c ds.Cursor) ds.Query { | 506 func (q *queryImpl) End(c ds.Cursor) ds.Query { |
418 » q = q.clone() | 507 » curs := queryCursor("") |
419 » curs := c.(queryCursor) | 508 » return q.checkMutateClone( |
420 » if !curs.Valid() { | 509 » » func() error { |
421 » » q.err = errors.New("datastore: invalid cursor") | 510 » » » ok := false |
422 » » return q | 511 » » » if curs, ok = c.(queryCursor); !ok { |
423 » } | 512 » » » » return fmt.Errorf("end cursor is unknown type: %
T", c) |
424 » q.end = curs | 513 » » » } |
425 » return q | 514 » » » if !curs.Valid() { |
| 515 » » » » return errors.New("datastore: invalid cursor") |
| 516 » » » } |
| 517 » » » return nil |
| 518 » » }, |
| 519 » » func(q *queryImpl) { |
| 520 » » » q.end = curs |
| 521 » » }) |
426 } | 522 } |
427 | 523 |
428 func (q *queryImpl) EventualConsistency() ds.Query { | 524 func (q *queryImpl) EventualConsistency() ds.Query { |
429 » q = q.clone() | 525 » return q.checkMutateClone( |
430 » q.eventualConsistency = true | 526 » » nil, func(q *queryImpl) { |
431 » return q | 527 » » » q.eventualConsistency = true |
432 } | 528 » » }) |
| 529 } |
OLD | NEW |