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" |
| 9 "encoding/base64" |
8 "errors" | 10 "errors" |
9 "fmt" | 11 "fmt" |
10 "math" | 12 "math" |
11 "strings" | 13 "strings" |
12 | 14 |
13 ds "github.com/luci/gae/service/datastore" | 15 ds "github.com/luci/gae/service/datastore" |
14 "github.com/luci/gae/service/datastore/serialize" | 16 "github.com/luci/gae/service/datastore/serialize" |
| 17 "github.com/luci/luci-go/common/cmpbin" |
15 ) | 18 ) |
16 | 19 |
17 // MaxQueryComponents was lifted from a hard-coded constant in dev_appserver. | 20 // 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 | 21 // No idea if it's a real limit or just a convenience in the current dev |
19 // appserver implementation. | 22 // appserver implementation. |
20 const MaxQueryComponents = 100 | 23 const MaxQueryComponents = 100 |
21 | 24 |
22 var errQueryDone = errors.New("query is done") | 25 var errQueryDone = errors.New("query is done") |
23 | 26 |
24 type queryOp int | 27 type queryOp int |
(...skipping 29 matching lines...) Expand all Loading... |
54 op = queryOpMap[toks[1]] | 57 op = queryOpMap[toks[1]] |
55 if op == qInvalid { | 58 if op == qInvalid { |
56 err = fmt.Errorf("datastore: invalid operator %q in filt
er %q", toks[1], f) | 59 err = fmt.Errorf("datastore: invalid operator %q in filt
er %q", toks[1], f) |
57 } else { | 60 } else { |
58 prop = toks[0] | 61 prop = toks[0] |
59 } | 62 } |
60 } | 63 } |
61 return | 64 return |
62 } | 65 } |
63 | 66 |
64 type queryCursor string | 67 // A queryCursor is: |
| 68 // {#orders} ++ IndexColumn* ++ RawRowData |
| 69 type queryCursor []byte |
65 | 70 |
66 func (q queryCursor) String() string { return string(q) } | 71 func (q queryCursor) String() string { |
67 func (q queryCursor) Valid() bool { return q != "" } | 72 » return base64.URLEncoding.EncodeToString([]byte(q)) |
| 73 } |
| 74 |
| 75 func (q queryCursor) decode() (cols []ds.IndexColumn, data []byte, err error) { |
| 76 » buf := bytes.NewBuffer([]byte(q)) |
| 77 » count, _, err := cmpbin.ReadUint(buf) |
| 78 » if err != nil { |
| 79 » » err = fmt.Errorf("invalid cursor") |
| 80 » » return |
| 81 » } |
| 82 |
| 83 » if count > ds.MaxIndexColumns { |
| 84 » » err = fmt.Errorf("invalid cursor") |
| 85 » » return |
| 86 » } |
| 87 |
| 88 » cols = make([]ds.IndexColumn, count) |
| 89 » for i := range cols { |
| 90 » » if cols[i], err = serialize.ReadIndexColumn(buf); err != nil { |
| 91 » » » err = fmt.Errorf("invalid cursor") |
| 92 » » » return |
| 93 » » } |
| 94 » } |
| 95 |
| 96 » data = buf.Bytes() |
| 97 » return |
| 98 } |
68 | 99 |
69 type queryIneqFilter struct { | 100 type queryIneqFilter struct { |
70 prop string | 101 prop string |
71 | 102 |
72 » low *string | 103 » low []byte |
73 » high *string | 104 » high []byte |
74 } | 105 } |
75 | 106 |
76 func increment(bstr string, positive bool) string { | 107 func increment(bstr []byte) []byte { |
| 108 » // Copy bstr |
| 109 » bstr = bjoin(bstr) |
| 110 |
77 lastIdx := len(bstr) - 1 | 111 lastIdx := len(bstr) - 1 |
78 » last := bstr[lastIdx] | 112 » if bstr[lastIdx] == 0xFF { |
79 » if positive { | 113 » » return append(bstr, 0) |
80 » » if last == 0xFF { | |
81 » » » return bstr + "\x00" | |
82 » » } | |
83 » » return bstr[:lastIdx] + string([]byte{last + 1}) | |
84 » } else { | |
85 » » if last == 0 { | |
86 » » » return bstr[:lastIdx-1] | |
87 » » } | |
88 » » return bstr[:lastIdx] + string([]byte{last - 1}) | |
89 } | 114 } |
| 115 bstr[lastIdx] += 1 |
| 116 return bstr |
90 } | 117 } |
91 | 118 |
92 // constrain 'folds' a new inequality into the current inequality filter. | 119 // constrain 'folds' a new inequality into the current inequality filter. |
93 // | 120 // |
94 // It will bump the high bound down, or the low bound up, assuming the incoming | 121 // It will bump the high bound down, or the low bound up, assuming the incoming |
95 // constraint does so. | 122 // constraint does so. |
96 // | 123 // |
97 // It returns true iff the filter is overconstrained (i.e. low > high) | 124 // It returns true iff the filter is overconstrained (i.e. low > high) |
98 func (q *queryIneqFilter) constrain(op queryOp, val string) bool { | 125 func (q *queryIneqFilter) constrain(op queryOp, val []byte) bool { |
99 switch op { | 126 switch op { |
| 127 case qLessEq: |
| 128 val = increment(val) |
| 129 fallthrough |
100 case qLessThan: | 130 case qLessThan: |
101 val = increment(val, true) | |
102 fallthrough | |
103 case qLessEq: | |
104 // adjust upper bound downwards | 131 // adjust upper bound downwards |
105 » » if q.high == nil || *q.high > val { | 132 » » if q.high == nil || bytes.Compare(q.high, val) > 0 { |
106 » » » q.high = &val | 133 » » » q.high = val |
107 } | 134 } |
108 | 135 |
109 case qGreaterThan: | 136 case qGreaterThan: |
110 » » val = increment(val, false) | 137 » » val = increment(val) |
111 fallthrough | 138 fallthrough |
112 case qGreaterEq: | 139 case qGreaterEq: |
113 // adjust lower bound upwards | 140 // adjust lower bound upwards |
114 » » if q.low == nil || *q.low < val { | 141 » » if q.low == nil || bytes.Compare(q.low, val) < 0 { |
115 » » » q.low = &val | 142 » » » q.low = val |
116 } | 143 } |
117 | 144 |
118 default: | 145 default: |
119 panic(fmt.Errorf("constrain cannot handle filter op %d", op)) | 146 panic(fmt.Errorf("constrain cannot handle filter op %d", op)) |
120 } | 147 } |
121 | 148 |
122 if q.low != nil && q.high != nil { | 149 if q.low != nil && q.high != nil { |
123 » » return *q.low > *q.high | 150 » » return bytes.Compare(q.low, q.high) >= 0 |
124 } | 151 } |
125 return false | 152 return false |
126 } | 153 } |
127 | 154 |
128 type queryImpl struct { | 155 type queryImpl struct { |
129 ns string | 156 ns string |
130 | 157 |
131 kind string | 158 kind string |
132 ancestor ds.Key | 159 ancestor ds.Key |
133 | 160 |
134 // prop -> encoded values | 161 // prop -> encoded values |
135 eqFilters map[string]map[string]struct{} | 162 eqFilters map[string]map[string]struct{} |
136 ineqFilter queryIneqFilter | 163 ineqFilter queryIneqFilter |
137 order []ds.IndexColumn | 164 order []ds.IndexColumn |
138 » project map[string]struct{} | 165 » project []string |
139 | 166 |
140 distinct bool | 167 distinct bool |
141 eventualConsistency bool | 168 eventualConsistency bool |
142 keysOnly bool | 169 keysOnly bool |
143 limit int32 | 170 limit int32 |
144 offset int32 | 171 offset int32 |
145 | 172 |
146 start queryCursor | 173 start queryCursor |
147 end queryCursor | 174 end queryCursor |
148 | 175 |
149 err error | 176 err error |
150 } | 177 } |
151 | 178 |
152 var _ ds.Query = (*queryImpl)(nil) | 179 var _ ds.Query = (*queryImpl)(nil) |
153 | 180 |
154 func (q *queryImpl) valid(ns string, isTxn bool) (done bool, err error) { | 181 func sortOrdersEqual(as, bs []ds.IndexColumn) bool { |
155 » if q.err == errQueryDone { | 182 » if len(as) != len(bs) { |
156 » » done = true | 183 » » return false |
157 » } else if q.err != nil { | 184 » } |
158 » » err = q.err | 185 » for i, a := range as { |
159 » } else if ns != q.ns { | 186 » » if a != bs[i] { |
160 » » err = errors.New( | 187 » » » return false |
| 188 » » } |
| 189 » } |
| 190 » return true |
| 191 } |
| 192 |
| 193 func (q *queryImpl) prep(ns string, isTxn bool) (*reducedQuery, error) { |
| 194 » if q.err != nil { |
| 195 » » return nil, q.err |
| 196 » } |
| 197 » if ns != q.ns { |
| 198 » » return nil, errors.New( |
161 "gae/memory: Namespace mismatched. Query and Datastore d
on't agree " + | 199 "gae/memory: Namespace mismatched. Query and Datastore d
on't agree " + |
162 "on the current namespace") | 200 "on the current namespace") |
163 } else if isTxn && q.ancestor == nil { | 201 } else if isTxn && q.ancestor == nil { |
164 » » err = errors.New( | 202 » » return nil, errors.New( |
165 "gae/memory: Only ancestor queries are allowed inside tr
ansactions") | 203 "gae/memory: Only ancestor queries are allowed inside tr
ansactions") |
166 } else if q.numComponents() > MaxQueryComponents { | 204 } else if q.numComponents() > MaxQueryComponents { |
167 » » err = fmt.Errorf( | 205 » » return nil, fmt.Errorf( |
168 "gae/memory: query is too large. may not have more than
"+ | 206 "gae/memory: query is too large. may not have more than
"+ |
169 "%d filters + sort orders + ancestor total: had
%d", | 207 "%d filters + sort orders + ancestor total: had
%d", |
170 MaxQueryComponents, q.numComponents()) | 208 MaxQueryComponents, q.numComponents()) |
171 } else if len(q.project) == 0 && q.distinct { | 209 } else if len(q.project) == 0 && q.distinct { |
172 // This must be delayed, because q.Distinct().Project("foo") is
a valid | 210 // This must be delayed, because q.Distinct().Project("foo") is
a valid |
173 // construction. If we checked this in Distinct, it could be too
early, and | 211 // construction. If we checked this in Distinct, it could be too
early, and |
174 // checking it in Project doesn't matter. | 212 // checking it in Project doesn't matter. |
175 » » err = errors.New( | 213 » » return nil, errors.New( |
176 "gae/memory: Distinct() only makes sense on projection q
ueries.") | 214 "gae/memory: Distinct() only makes sense on projection q
ueries.") |
177 } | 215 } |
178 » return | 216 » ret := &reducedQuery{ |
| 217 » » ns: q.ns, |
| 218 » » kind: q.kind, |
| 219 » » eqFilters: q.eqFilters, |
| 220 » » suffixFormat: q.order, |
| 221 » } |
| 222 » if len(ret.suffixFormat) == 0 && q.ineqFilter.prop != "" { |
| 223 » » ret.suffixFormat = []ds.IndexColumn{{Property: q.ineqFilter.prop
}} |
| 224 » } |
| 225 » if q.ancestor != nil { |
| 226 » » ret.ancestor = serialize.ToBytes(q.ancestor) |
| 227 » } |
| 228 |
| 229 » // The inequality is specified in natural (ascending) order in the query
, but |
| 230 » // the order information may indicate to use an descending index column.
If |
| 231 » // that's the case, then we must invert, swap and increment the inequali
ty |
| 232 » // endpoints. |
| 233 » // |
| 234 » // Invert so that the desired numbers are represented correctly in the i
ndex. |
| 235 » // Swap so that our iterators still go from >= low to < high. |
| 236 » // Increment so that >= and < get correctly bounded (since the iterator
is |
| 237 » // still using natrual ordering) |
| 238 » shouldInvertIneq := false |
| 239 » if len(ret.suffixFormat) > 0 && ret.suffixFormat[0].Direction == ds.DESC
ENDING { |
| 240 » » shouldInvertIneq = true |
| 241 » } |
| 242 |
| 243 » err := error(nil) |
| 244 » cols := []ds.IndexColumn(nil) |
| 245 » if q.start != nil { |
| 246 » » cols, ret.low, err = q.start.decode() |
| 247 » » if err != nil { |
| 248 » » » return nil, err |
| 249 » » } |
| 250 » » if !sortOrdersEqual(cols, ret.suffixFormat) { |
| 251 » » » return nil, errors.New("gae/memory: start cursor is inva
lid.") |
| 252 » » } |
| 253 » } else { |
| 254 » » if shouldInvertIneq { |
| 255 » » » ret.high = increment(invert(q.ineqFilter.low)) |
| 256 » » } else { |
| 257 » » » ret.low = q.ineqFilter.low |
| 258 » » } |
| 259 » } |
| 260 |
| 261 » if q.end != nil { |
| 262 » » cols, ret.high, err = q.end.decode() |
| 263 » » if err != nil { |
| 264 » » » return nil, err |
| 265 » » } |
| 266 » » if !sortOrdersEqual(cols, ret.suffixFormat) { |
| 267 » » » return nil, errors.New("gae/memory: end cursor is invali
d.") |
| 268 » » } |
| 269 » } else { |
| 270 » » if shouldInvertIneq { |
| 271 » » » ret.low = increment(invert(q.ineqFilter.high)) |
| 272 » » } else { |
| 273 » » » ret.high = q.ineqFilter.high |
| 274 » » } |
| 275 » } |
| 276 |
| 277 » // Add any projection columns not mentioned in the user-defined order as |
| 278 » // ASCENDING orders. Technically we could be smart and automatically use |
| 279 » // a DESCENDING ordered index, if it fit, but the logic gets insane, sin
ce all |
| 280 » // suffixes of all used indexes need to be PRECISELY equal (and so you'd
have |
| 281 » // to hunt/invalidate/something to find the combination of indexes that
are |
| 282 » // compatible with each other as well as the query). If you want to use |
| 283 » // a DESCENDING column, just add it to the user sort order, and this loo
p will |
| 284 » // not synthesize a new sort order for it. |
| 285 » originalStop := len(ret.suffixFormat) |
| 286 » for _, p := range q.project { |
| 287 » » needAdd := true |
| 288 » » // originalStop prevents this loop from getting longer every tim
e we add |
| 289 » » // a projected property. |
| 290 » » for _, col := range ret.suffixFormat[:originalStop] { |
| 291 » » » if col.Property == p { |
| 292 » » » » needAdd = false |
| 293 » » » » break |
| 294 » » » } |
| 295 » » } |
| 296 » » if needAdd { |
| 297 » » » ret.suffixFormat = append(ret.suffixFormat, ds.IndexColu
mn{Property: p}) |
| 298 » » } |
| 299 » } |
| 300 |
| 301 » return ret, nil |
| 302 |
179 } | 303 } |
180 | 304 |
181 func (q *queryImpl) numComponents() int { | 305 func (q *queryImpl) numComponents() int { |
182 numComponents := len(q.order) | 306 numComponents := len(q.order) |
183 if q.ineqFilter.prop != "" { | 307 if q.ineqFilter.prop != "" { |
184 if q.ineqFilter.low != nil { | 308 if q.ineqFilter.low != nil { |
185 numComponents++ | 309 numComponents++ |
186 } | 310 } |
187 if q.ineqFilter.high != nil { | 311 if q.ineqFilter.high != nil { |
188 numComponents++ | 312 numComponents++ |
189 } | 313 } |
190 } | 314 } |
191 for _, v := range q.eqFilters { | 315 for _, v := range q.eqFilters { |
192 numComponents += len(v) | 316 numComponents += len(v) |
193 } | 317 } |
194 if q.ancestor != nil { | 318 if q.ancestor != nil { |
195 numComponents++ | 319 numComponents++ |
196 } | 320 } |
197 return numComponents | 321 return numComponents |
198 } | 322 } |
199 | 323 |
200 func (q *queryImpl) calculateIndex() *ds.IndexDefinition { | |
201 // as a nod to simplicity in this code, we'll require that a single inde
x | |
202 // is able to service the entire query. E.g. no zigzag merge joins or | |
203 // multiqueries. This will mean that the user will need to rely on | |
204 // dev_appserver to tell them what indicies they need for real, and for
thier | |
205 // tests they'll need to specify the missing composite indices manually. | |
206 // | |
207 // This COULD lead to an exploding indicies problem, but we can fix that
when | |
208 // we get to it. | |
209 | |
210 //sortOrders := []qSortBy{} | |
211 | |
212 return nil | |
213 } | |
214 | |
215 // checkMutateClone sees if the query has an error. If not, it clones the query, | 324 // checkMutateClone sees if the query has an error. If not, it clones the query, |
216 // and assigns the output of `check` to the query error slot. If check returns | 325 // and assigns the output of `check` to the query error slot. If check returns |
217 // nil, it calls `mutate` on the cloned query. The (possibly new) query is then | 326 // nil, it calls `mutate` on the cloned query. The (possibly new) query is then |
218 // returned. | 327 // returned. |
219 func (q *queryImpl) checkMutateClone(check func() error, mutate func(*queryImpl)
) *queryImpl { | 328 func (q *queryImpl) checkMutateClone(check func() error, mutate func(*queryImpl)
) *queryImpl { |
220 if q.err != nil { | 329 if q.err != nil { |
221 return q | 330 return q |
222 } | 331 } |
223 nq := *q | 332 nq := *q |
224 nq.eqFilters = make(map[string]map[string]struct{}, len(q.eqFilters)) | 333 nq.eqFilters = make(map[string]map[string]struct{}, len(q.eqFilters)) |
225 for prop, vals := range q.eqFilters { | 334 for prop, vals := range q.eqFilters { |
226 nq.eqFilters[prop] = make(map[string]struct{}, len(vals)) | 335 nq.eqFilters[prop] = make(map[string]struct{}, len(vals)) |
227 for v := range vals { | 336 for v := range vals { |
228 nq.eqFilters[prop][v] = struct{}{} | 337 nq.eqFilters[prop][v] = struct{}{} |
229 } | 338 } |
230 } | 339 } |
231 nq.order = make([]ds.IndexColumn, len(q.order)) | 340 nq.order = make([]ds.IndexColumn, len(q.order)) |
232 copy(nq.order, q.order) | 341 copy(nq.order, q.order) |
233 » nq.project = make(map[string]struct{}, len(q.project)) | 342 » nq.project = make([]string, len(q.project)) |
234 » for f := range q.project { | 343 » copy(nq.project, q.project) |
235 » » nq.project[f] = struct{}{} | |
236 » } | |
237 if check != nil { | 344 if check != nil { |
238 nq.err = check() | 345 nq.err = check() |
239 } | 346 } |
240 if nq.err == nil { | 347 if nq.err == nil { |
241 mutate(&nq) | 348 mutate(&nq) |
242 } | 349 } |
243 return &nq | 350 return &nq |
244 } | 351 } |
245 | 352 |
246 func (q *queryImpl) Ancestor(k ds.Key) ds.Query { | 353 func (q *queryImpl) Ancestor(k ds.Key) ds.Query { |
247 return q.checkMutateClone( | 354 return q.checkMutateClone( |
248 func() error { | 355 func() error { |
249 if k == nil { | 356 if k == nil { |
250 // SDK has an explicit nil-check | 357 // SDK has an explicit nil-check |
251 return errors.New("datastore: nil query ancestor
") | 358 return errors.New("datastore: nil query ancestor
") |
252 } | 359 } |
| 360 if k.Namespace() != q.ns { |
| 361 return fmt.Errorf("bad namespace: %q (expected %
q)", k.Namespace(), q.ns) |
| 362 } |
253 if !k.Valid(false, globalAppID, q.ns) { | 363 if !k.Valid(false, globalAppID, q.ns) { |
254 // technically the SDK implementation does a Wei
rd Thing (tm) if both the | 364 // technically the SDK implementation does a Wei
rd Thing (tm) if both the |
255 // stringID and intID are set on a key; it only
serializes the stringID in | 365 // stringID and intID are set on a key; it only
serializes the stringID in |
256 // the proto. This means that if you set the Anc
estor to an invalid key, | 366 // the proto. This means that if you set the Anc
estor to an invalid key, |
257 // you'll never actually hear about it. Instead
of doing that insanity, we | 367 // you'll never actually hear about it. Instead
of doing that insanity, we |
258 // just swap to an error here. | 368 // just swap to an error here. |
259 return ds.ErrInvalidKey | 369 return ds.ErrInvalidKey |
260 } | 370 } |
261 if k.Namespace() != q.ns { | |
262 return fmt.Errorf("bad namespace: %q (expected %
q)", k.Namespace(), q.ns) | |
263 } | |
264 if q.ancestor != nil { | 371 if q.ancestor != nil { |
265 return errors.New("cannot have more than one anc
estor") | 372 return errors.New("cannot have more than one anc
estor") |
266 } | 373 } |
267 return nil | 374 return nil |
268 }, | 375 }, |
269 func(q *queryImpl) { | 376 func(q *queryImpl) { |
270 q.ancestor = k | 377 q.ancestor = k |
271 }) | 378 }) |
272 } | 379 } |
273 | 380 |
274 func (q *queryImpl) Distinct() ds.Query { | 381 func (q *queryImpl) Distinct() ds.Query { |
275 return q.checkMutateClone(nil, func(q *queryImpl) { | 382 return q.checkMutateClone(nil, func(q *queryImpl) { |
276 q.distinct = true | 383 q.distinct = true |
277 }) | 384 }) |
278 } | 385 } |
279 | 386 |
280 func (q *queryImpl) Filter(fStr string, val interface{}) ds.Query { | 387 func (q *queryImpl) Filter(fStr string, val interface{}) ds.Query { |
281 prop := "" | 388 prop := "" |
282 op := qInvalid | 389 op := qInvalid |
283 » binVal := "" | 390 » binVal := []byte(nil) |
284 return q.checkMutateClone( | 391 return q.checkMutateClone( |
285 func() error { | 392 func() error { |
286 var err error | 393 var err error |
287 prop, op, err = parseFilter(fStr) | 394 prop, op, err = parseFilter(fStr) |
288 if err != nil { | 395 if err != nil { |
289 return err | 396 return err |
290 } | 397 } |
291 | 398 |
292 if q.kind == "" && prop != "__key__" { | 399 if q.kind == "" && prop != "__key__" { |
293 // https://cloud.google.com/appengine/docs/go/da
tastore/queries#Go_Kindless_queries | 400 // https://cloud.google.com/appengine/docs/go/da
tastore/queries#Go_Kindless_queries |
(...skipping 27 matching lines...) Expand all Loading... |
321 if q.ineqFilter.prop != "" && q.ineqFilter.prop
!= prop { | 428 if q.ineqFilter.prop != "" && q.ineqFilter.prop
!= prop { |
322 return fmt.Errorf( | 429 return fmt.Errorf( |
323 "inequality filters on multiple
properties: %q and %q", | 430 "inequality filters on multiple
properties: %q and %q", |
324 q.ineqFilter.prop, prop) | 431 q.ineqFilter.prop, prop) |
325 } | 432 } |
326 if len(q.order) > 0 && q.order[0].Property != pr
op { | 433 if len(q.order) > 0 && q.order[0].Property != pr
op { |
327 return fmt.Errorf( | 434 return fmt.Errorf( |
328 "first sort order must match ine
quality filter: %q v %q", | 435 "first sort order must match ine
quality filter: %q v %q", |
329 q.order[0].Property, prop) | 436 q.order[0].Property, prop) |
330 } | 437 } |
331 » » » } else if _, ok := q.project[prop]; ok { | 438 » » » } else { |
332 » » » » return fmt.Errorf( | 439 » » » » for _, p := range q.project { |
333 » » » » » "cannot project on field which is used i
n an equality filter: %q", | 440 » » » » » if p == prop { |
334 » » » » » prop) | 441 » » » » » » return fmt.Errorf( |
| 442 » » » » » » » "cannot project on field
which is used in an equality filter: %q", |
| 443 » » » » » » » prop) |
| 444 » » » » » } |
| 445 » » » » } |
335 } | 446 } |
336 » » » binVal = string(serialize.ToBytes(p)) | 447 » » » binVal = serialize.ToBytes(p) |
337 return err | 448 return err |
338 }, | 449 }, |
339 func(q *queryImpl) { | 450 func(q *queryImpl) { |
340 if op == qEqual { | 451 if op == qEqual { |
341 // add it to eq filters | 452 // add it to eq filters |
342 if _, ok := q.eqFilters[prop]; !ok { | 453 if _, ok := q.eqFilters[prop]; !ok { |
343 » » » » » q.eqFilters[prop] = map[string]struct{}{
binVal: {}} | 454 » » » » » q.eqFilters[prop] = map[string]struct{}{
string(binVal): {}} |
344 } else { | 455 } else { |
345 » » » » » q.eqFilters[prop][binVal] = struct{}{} | 456 » » » » » q.eqFilters[prop][string(binVal)] = stru
ct{}{} |
346 } | 457 } |
347 | 458 |
348 // remove it from sort orders. | 459 // remove it from sort orders. |
349 // https://cloud.google.com/appengine/docs/go/da
tastore/queries#sort_orders_are_ignored_on_properties_with_equality_filters | 460 // https://cloud.google.com/appengine/docs/go/da
tastore/queries#sort_orders_are_ignored_on_properties_with_equality_filters |
350 toRm := -1 | 461 toRm := -1 |
351 for i, o := range q.order { | 462 for i, o := range q.order { |
352 if o.Property == prop { | 463 if o.Property == prop { |
353 toRm = i | 464 toRm = i |
354 break | 465 break |
355 } | 466 } |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
412 } | 523 } |
413 }) | 524 }) |
414 } | 525 } |
415 | 526 |
416 func (q *queryImpl) Project(fieldName ...string) ds.Query { | 527 func (q *queryImpl) Project(fieldName ...string) ds.Query { |
417 return q.checkMutateClone( | 528 return q.checkMutateClone( |
418 func() error { | 529 func() error { |
419 if q.keysOnly { | 530 if q.keysOnly { |
420 return errors.New("cannot project a keysOnly que
ry") | 531 return errors.New("cannot project a keysOnly que
ry") |
421 } | 532 } |
| 533 dupCheck := map[string]struct{}{} |
422 for _, f := range fieldName { | 534 for _, f := range fieldName { |
| 535 if _, ok := dupCheck[f]; ok { |
| 536 return fmt.Errorf("cannot project on the
same field twice: %q", f) |
| 537 } |
| 538 dupCheck[f] = struct{}{} |
423 if f == "" { | 539 if f == "" { |
424 return errors.New("cannot project on an
empty field name") | 540 return errors.New("cannot project on an
empty field name") |
425 } | 541 } |
426 » » » » if strings.HasPrefix(f, "__") && strings.HasSuff
ix(f, "__") { | 542 » » » » if f == "__key__" { |
427 » » » » » return fmt.Errorf("cannot project on %q"
, f) | 543 » » » » » return fmt.Errorf("cannot project on __k
ey__") |
428 } | 544 } |
429 if _, ok := q.eqFilters[f]; ok { | 545 if _, ok := q.eqFilters[f]; ok { |
430 return fmt.Errorf( | 546 return fmt.Errorf( |
431 "cannot project on field which i
s used in an equality filter: %q", f) | 547 "cannot project on field which i
s used in an equality filter: %q", f) |
432 } | 548 } |
| 549 for _, p := range q.project { |
| 550 if p == f { |
| 551 return fmt.Errorf("cannot projec
t on the same field twice: %q", f) |
| 552 } |
| 553 } |
433 } | 554 } |
434 return nil | 555 return nil |
435 }, | 556 }, |
436 func(q *queryImpl) { | 557 func(q *queryImpl) { |
437 » » » for _, f := range fieldName { | 558 » » » q.project = append(q.project, fieldName...) |
438 » » » » q.project[f] = struct{}{} | |
439 » » » } | |
440 }) | 559 }) |
441 } | 560 } |
442 | 561 |
443 func (q *queryImpl) KeysOnly() ds.Query { | 562 func (q *queryImpl) KeysOnly() ds.Query { |
444 return q.checkMutateClone( | 563 return q.checkMutateClone( |
445 func() error { | 564 func() error { |
446 if len(q.project) != 0 { | 565 if len(q.project) != 0 { |
447 return errors.New("cannot project a keysOnly que
ry") | 566 return errors.New("cannot project a keysOnly que
ry") |
448 } | 567 } |
449 return nil | 568 return nil |
(...skipping 25 matching lines...) Expand all Loading... |
475 if offset > math.MaxInt32 { | 594 if offset > math.MaxInt32 { |
476 return errors.New("datastore: query offset overf
low") | 595 return errors.New("datastore: query offset overf
low") |
477 } | 596 } |
478 return nil | 597 return nil |
479 }, | 598 }, |
480 func(q *queryImpl) { | 599 func(q *queryImpl) { |
481 q.offset = int32(offset) | 600 q.offset = int32(offset) |
482 }) | 601 }) |
483 } | 602 } |
484 | 603 |
| 604 func queryCursorCheck(ns, flavor string, current queryCursor, newCursor ds.Curso
r) (queryCursor, error) { |
| 605 if current != nil { |
| 606 return nil, fmt.Errorf("%s cursor is multiply defined", flavor) |
| 607 } |
| 608 curs, ok := newCursor.(queryCursor) |
| 609 if !ok { |
| 610 return nil, fmt.Errorf("%s cursor is unknown type: %T", flavor,
curs) |
| 611 } |
| 612 _, _, err := curs.decode() |
| 613 return curs, err |
| 614 } |
| 615 |
485 func (q *queryImpl) Start(c ds.Cursor) ds.Query { | 616 func (q *queryImpl) Start(c ds.Cursor) ds.Query { |
486 » curs := queryCursor("") | 617 » curs := queryCursor(nil) |
487 return q.checkMutateClone( | 618 return q.checkMutateClone( |
488 » » func() error { | 619 » » func() (err error) { |
489 » » » ok := false | 620 » » » curs, err = queryCursorCheck(q.ns, "start", q.start, c) |
490 » » » if curs, ok = c.(queryCursor); !ok { | 621 » » » return |
491 » » » » return fmt.Errorf("start cursor is unknown type:
%T", c) | |
492 » » » } | |
493 » » » if !curs.Valid() { | |
494 » » » » return errors.New("datastore: invalid cursor") | |
495 » » » } | |
496 » » » return nil | |
497 }, | 622 }, |
498 func(q *queryImpl) { | 623 func(q *queryImpl) { |
499 q.start = curs | 624 q.start = curs |
500 }) | 625 }) |
501 } | 626 } |
502 | 627 |
503 func (q *queryImpl) End(c ds.Cursor) ds.Query { | 628 func (q *queryImpl) End(c ds.Cursor) ds.Query { |
504 » curs := queryCursor("") | 629 » curs := queryCursor(nil) |
505 return q.checkMutateClone( | 630 return q.checkMutateClone( |
506 » » func() error { | 631 » » func() (err error) { |
507 » » » ok := false | 632 » » » curs, err = queryCursorCheck(q.ns, "end", q.end, c) |
508 » » » if curs, ok = c.(queryCursor); !ok { | 633 » » » return |
509 » » » » return fmt.Errorf("end cursor is unknown type: %
T", c) | |
510 » » » } | |
511 » » » if !curs.Valid() { | |
512 » » » » return errors.New("datastore: invalid cursor") | |
513 » » » } | |
514 » » » return nil | |
515 }, | 634 }, |
516 func(q *queryImpl) { | 635 func(q *queryImpl) { |
517 q.end = curs | 636 q.end = curs |
518 }) | 637 }) |
519 } | 638 } |
520 | 639 |
521 func (q *queryImpl) EventualConsistency() ds.Query { | 640 func (q *queryImpl) EventualConsistency() ds.Query { |
522 return q.checkMutateClone( | 641 return q.checkMutateClone( |
523 nil, func(q *queryImpl) { | 642 nil, func(q *queryImpl) { |
524 q.eventualConsistency = true | 643 q.eventualConsistency = true |
525 }) | 644 }) |
526 } | 645 } |
OLD | NEW |