| 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 "encoding/base64" | 9 "encoding/base64" |
| 10 "errors" | 10 "errors" |
| 11 "fmt" | 11 "fmt" |
| 12 "math" | |
| 13 "strings" | |
| 14 | 12 |
| 15 ds "github.com/luci/gae/service/datastore" | 13 ds "github.com/luci/gae/service/datastore" |
| 16 "github.com/luci/gae/service/datastore/serialize" | 14 "github.com/luci/gae/service/datastore/serialize" |
| 17 "github.com/luci/luci-go/common/cmpbin" | 15 "github.com/luci/luci-go/common/cmpbin" |
| 18 "github.com/luci/luci-go/common/stringset" | 16 "github.com/luci/luci-go/common/stringset" |
| 19 ) | 17 ) |
| 20 | 18 |
| 21 // MaxQueryComponents was lifted from a hard-coded constant in dev_appserver. | 19 // MaxQueryComponents was lifted from a hard-coded constant in dev_appserver. |
| 22 // No idea if it's a real limit or just a convenience in the current dev | 20 // No idea if it's a real limit or just a convenience in the current dev |
| 23 // appserver implementation. | 21 // appserver implementation. |
| 24 const MaxQueryComponents = 100 | 22 const MaxQueryComponents = 100 |
| 25 | 23 |
| 26 var errQueryDone = errors.New("query is done") | 24 // MaxIndexColumns is the maximum number of index columns we're willing to |
| 27 | 25 // support. |
| 28 type queryOp int | 26 const MaxIndexColumns = 64 |
| 29 | |
| 30 const ( | |
| 31 » qInvalid queryOp = iota | |
| 32 » qEqual | |
| 33 » qLessThan | |
| 34 » qLessEq | |
| 35 » qGreaterEq | |
| 36 » qGreaterThan | |
| 37 ) | |
| 38 | |
| 39 var queryOpMap = map[string]queryOp{ | |
| 40 » "=": qEqual, | |
| 41 » "<": qLessThan, | |
| 42 » "<=": qLessEq, | |
| 43 » ">=": qGreaterEq, | |
| 44 » ">": qGreaterThan, | |
| 45 } | |
| 46 | |
| 47 type queryFilter struct { | |
| 48 » prop string | |
| 49 » op queryOp | |
| 50 » value interface{} | |
| 51 } | |
| 52 | |
| 53 func parseFilter(f string) (prop string, op queryOp, err error) { | |
| 54 » toks := strings.SplitN(strings.TrimSpace(f), " ", 2) | |
| 55 » if len(toks) != 2 { | |
| 56 » » err = errors.New("datastore: invalid filter: " + f) | |
| 57 » } else { | |
| 58 » » op = queryOpMap[toks[1]] | |
| 59 » » if op == qInvalid { | |
| 60 » » » err = fmt.Errorf("datastore: invalid operator %q in filt
er %q", toks[1], f) | |
| 61 » » } else { | |
| 62 » » » prop = toks[0] | |
| 63 » » } | |
| 64 » } | |
| 65 » return | |
| 66 } | |
| 67 | 27 |
| 68 // A queryCursor is: | 28 // A queryCursor is: |
| 69 // {#orders} ++ IndexColumn* ++ RawRowData | 29 // {#orders} ++ IndexColumn* ++ RawRowData |
| 70 // IndexColumn will always contain __key__ as the last column, and so #orders | 30 // IndexColumn will always contain __key__ as the last column, and so #orders |
| 71 // must always be >= 1 | 31 // must always be >= 1 |
| 72 type queryCursor []byte | 32 type queryCursor []byte |
| 73 | 33 |
| 74 func newCursor(s string) (ds.Cursor, error) { | 34 func newCursor(s string) (ds.Cursor, error) { |
| 75 d, err := base64.URLEncoding.DecodeString(s) | 35 d, err := base64.URLEncoding.DecodeString(s) |
| 76 if err != nil { | 36 if err != nil { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 87 | 47 |
| 88 // decode returns the encoded IndexColumns, the raw row (cursor) data, or an | 48 // decode returns the encoded IndexColumns, the raw row (cursor) data, or an |
| 89 // error. | 49 // error. |
| 90 func (q queryCursor) decode() ([]ds.IndexColumn, []byte, error) { | 50 func (q queryCursor) decode() ([]ds.IndexColumn, []byte, error) { |
| 91 buf := bytes.NewBuffer([]byte(q)) | 51 buf := bytes.NewBuffer([]byte(q)) |
| 92 count, _, err := cmpbin.ReadUint(buf) | 52 count, _, err := cmpbin.ReadUint(buf) |
| 93 if err != nil { | 53 if err != nil { |
| 94 return nil, nil, fmt.Errorf("invalid cursor: bad prefix number") | 54 return nil, nil, fmt.Errorf("invalid cursor: bad prefix number") |
| 95 } | 55 } |
| 96 | 56 |
| 97 » if count == 0 || count > ds.MaxIndexColumns { | 57 » if count == 0 || count > MaxIndexColumns { |
| 98 return nil, nil, fmt.Errorf("invalid cursor: bad column count %d
", count) | 58 return nil, nil, fmt.Errorf("invalid cursor: bad column count %d
", count) |
| 99 } | 59 } |
| 100 | 60 |
| 101 if count == 0 { | 61 if count == 0 { |
| 102 return nil, nil, fmt.Errorf("invalid cursor: zero prefix number"
) | 62 return nil, nil, fmt.Errorf("invalid cursor: zero prefix number"
) |
| 103 } | 63 } |
| 104 | 64 |
| 105 cols := make([]ds.IndexColumn, count) | 65 cols := make([]ds.IndexColumn, count) |
| 106 for i := range cols { | 66 for i := range cols { |
| 107 if cols[i], err = serialize.ReadIndexColumn(buf); err != nil { | 67 if cols[i], err = serialize.ReadIndexColumn(buf); err != nil { |
| 108 return nil, nil, fmt.Errorf("invalid cursor: unable to d
ecode IndexColumn %d: %s", i, err) | 68 return nil, nil, fmt.Errorf("invalid cursor: unable to d
ecode IndexColumn %d: %s", i, err) |
| 109 } | 69 } |
| 110 } | 70 } |
| 111 | 71 |
| 112 if cols[len(cols)-1].Property != "__key__" { | 72 if cols[len(cols)-1].Property != "__key__" { |
| 113 return nil, nil, fmt.Errorf("invalid cursor: last column was not
__key__: %v", cols[len(cols)-1]) | 73 return nil, nil, fmt.Errorf("invalid cursor: last column was not
__key__: %v", cols[len(cols)-1]) |
| 114 } | 74 } |
| 115 | 75 |
| 116 return cols, buf.Bytes(), nil | 76 return cols, buf.Bytes(), nil |
| 117 } | 77 } |
| 118 | 78 |
| 119 type queryIneqFilter struct { | |
| 120 prop string | |
| 121 | |
| 122 start []byte | |
| 123 end []byte | |
| 124 } | |
| 125 | |
| 126 // constrain 'folds' a new inequality into the current inequality filter. | |
| 127 // | |
| 128 // It will bump the end bound down, or the start bound up, assuming the incoming | |
| 129 // constraint does so. | |
| 130 // | |
| 131 // It returns true iff the filter is overconstrained (i.e. start > end) | |
| 132 func (q *queryIneqFilter) constrain(op queryOp, val []byte) bool { | |
| 133 switch op { | |
| 134 case qLessEq: | |
| 135 val = increment(val) | |
| 136 fallthrough | |
| 137 case qLessThan: | |
| 138 // adjust upper bound downwards | |
| 139 if q.end == nil || bytes.Compare(q.end, val) > 0 { | |
| 140 q.end = val | |
| 141 } | |
| 142 | |
| 143 case qGreaterThan: | |
| 144 val = increment(val) | |
| 145 fallthrough | |
| 146 case qGreaterEq: | |
| 147 // adjust lower bound upwards | |
| 148 if q.start == nil || bytes.Compare(q.start, val) < 0 { | |
| 149 q.start = val | |
| 150 } | |
| 151 | |
| 152 default: | |
| 153 impossible(fmt.Errorf("constrain cannot handle filter op %d", op
)) | |
| 154 } | |
| 155 | |
| 156 if q.start != nil && q.end != nil { | |
| 157 return bytes.Compare(q.start, q.end) >= 0 | |
| 158 } | |
| 159 return false | |
| 160 } | |
| 161 | |
| 162 type queryImpl struct { | |
| 163 ns string | |
| 164 | |
| 165 kind string | |
| 166 | |
| 167 // prop -> encoded values (which are ds.Property objects) | |
| 168 // "__ancestor__" is the key for Ancestor queries. | |
| 169 eqFilters map[string]stringset.Set | |
| 170 ineqFilter queryIneqFilter | |
| 171 order []ds.IndexColumn | |
| 172 startCursor []byte | |
| 173 startCursorColumns []ds.IndexColumn | |
| 174 endCursor []byte | |
| 175 endCursorColumns []ds.IndexColumn | |
| 176 | |
| 177 // All of these are applied in post (e.g. not during the native index sc
an). | |
| 178 distinct bool | |
| 179 eventualConsistency bool | |
| 180 keysOnly bool | |
| 181 limitSet bool | |
| 182 limit int32 | |
| 183 offset int32 | |
| 184 project []string | |
| 185 | |
| 186 err error | |
| 187 } | |
| 188 | |
| 189 var _ ds.Query = (*queryImpl)(nil) | |
| 190 | |
| 191 func sortOrdersEqual(as, bs []ds.IndexColumn) bool { | 79 func sortOrdersEqual(as, bs []ds.IndexColumn) bool { |
| 192 if len(as) != len(bs) { | 80 if len(as) != len(bs) { |
| 193 return false | 81 return false |
| 194 } | 82 } |
| 195 for i, a := range as { | 83 for i, a := range as { |
| 196 if a != bs[i] { | 84 if a != bs[i] { |
| 197 return false | 85 return false |
| 198 } | 86 } |
| 199 } | 87 } |
| 200 return true | 88 return true |
| 201 } | 89 } |
| 202 | 90 |
| 203 func (q *queryImpl) reduce(ns string, isTxn bool) (*reducedQuery, error) { | 91 func numComponents(fq *ds.FinalizedQuery) int { |
| 204 » if q.err != nil { | 92 » numComponents := len(fq.Orders()) |
| 205 » » return nil, q.err | 93 » if p, _, _ := fq.IneqFilterLow(); p != "" { |
| 94 » » numComponents++ |
| 206 } | 95 } |
| 207 » if ns != q.ns { | 96 » if p, _, _ := fq.IneqFilterHigh(); p != "" { |
| 208 » » return nil, errors.New( | 97 » » numComponents++ |
| 209 » » » "gae/memory: Namespace mismatched. Query and Datastore d
on't agree " + | |
| 210 » » » » "on the current namespace") | |
| 211 } | 98 } |
| 212 » if isTxn && q.eqFilters["__ancestor__"] == nil { | 99 » for _, v := range fq.EqFilters() { |
| 213 » » return nil, errors.New( | 100 » » numComponents += v.Len() |
| 214 » » » "gae/memory: Only ancestor queries are allowed inside tr
ansactions") | |
| 215 } | 101 } |
| 216 » if q.numComponents() > MaxQueryComponents { | 102 » return numComponents |
| 103 } |
| 104 |
| 105 func reduce(fq *ds.FinalizedQuery, ns string, isTxn bool) (*reducedQuery, error)
{ |
| 106 » if err := fq.Valid(globalAppID, ns); err != nil { |
| 107 » » return nil, err |
| 108 » } |
| 109 » if isTxn && fq.Ancestor() == nil { |
| 110 » » return nil, fmt.Errorf("queries within a transaction must includ
e an Ancestor filter") |
| 111 » } |
| 112 » if num := numComponents(fq); num > MaxQueryComponents { |
| 217 return nil, fmt.Errorf( | 113 return nil, fmt.Errorf( |
| 218 "gae/memory: query is too large. may not have more than
"+ | 114 "gae/memory: query is too large. may not have more than
"+ |
| 219 "%d filters + sort orders + ancestor total: had
%d", | 115 "%d filters + sort orders + ancestor total: had
%d", |
| 220 » » » MaxQueryComponents, q.numComponents()) | 116 » » » MaxQueryComponents, num) |
| 221 } | 117 } |
| 222 » if len(q.project) == 0 && q.distinct { | 118 |
| 223 » » // This must be delayed, because q.Distinct().Project("foo") is
a valid | 119 » ret := &reducedQuery{ |
| 224 » » // construction. If we checked this in Distinct, it could be too
early, and | 120 » » ns: ns, |
| 225 » » // checking it in Project doesn't matter. | 121 » » kind: fq.Kind(), |
| 226 » » return nil, errors.New( | 122 » » suffixFormat: fq.Orders(), |
| 227 » » » "gae/memory: Distinct() only makes sense on projection q
ueries.") | |
| 228 } | 123 } |
| 229 » if q.eqFilters["__ancestor__"] != nil && q.ineqFilter.prop == "__key__"
{ | 124 |
| 230 » » ancS, _ := q.eqFilters["__ancestor__"].Peek() | 125 » eqFilts := fq.EqFilters() |
| 231 » » anc := []byte(ancS[:len(ancS)-1]) | 126 » ret.eqFilters = make(map[string]stringset.Set, len(eqFilts)) |
| 232 » » if q.ineqFilter.start != nil && !bytes.HasPrefix(q.ineqFilter.st
art, anc) { | 127 » for prop, vals := range eqFilts { |
| 233 » » » return nil, errors.New( | 128 » » sVals := stringset.New(len(vals)) |
| 234 » » » » "gae/memory: __key__ inequality filter has a val
ue outside of Ancestor()") | 129 » » for _, v := range vals { |
| 130 » » » sVals.Add(string(serialize.ToBytes(v))) |
| 235 } | 131 } |
| 236 » » if q.ineqFilter.end != nil && !bytes.HasPrefix(q.ineqFilter.end,
anc) { | 132 » » ret.eqFilters[prop] = sVals |
| 237 » » » return nil, errors.New( | 133 » } |
| 238 » » » » "gae/memory: __key__ inequality filter has a val
ue outside of Ancestor()") | 134 |
| 135 » // Pick up the start/end range from the inequalities, if any. |
| 136 » // |
| 137 » // start and end in the reducedQuery are normalized so that `start >= |
| 138 » // X < end`. Because of that, we need to tweak the inequality filters |
| 139 » // contained in the query if they use the > or <= operators. |
| 140 » startD := []byte(nil) |
| 141 » endD := []byte(nil) |
| 142 » if ineqProp := fq.IneqFilterProp(); ineqProp != "" { |
| 143 » » _, startOp, startV := fq.IneqFilterLow() |
| 144 » » if startOp != "" { |
| 145 » » » startD = serialize.ToBytes(startV) |
| 146 » » » if startOp == ">" { |
| 147 » » » » startD = increment(startD) |
| 148 » » » } |
| 149 » » } |
| 150 |
| 151 » » _, endOp, endV := fq.IneqFilterHigh() |
| 152 » » if endOp != "" { |
| 153 » » » endD = serialize.ToBytes(endV) |
| 154 » » » if endOp == "<=" { |
| 155 » » » » endD = increment(endD) |
| 156 » » » } |
| 157 » » } |
| 158 |
| 159 » » // The inequality is specified in natural (ascending) order in t
he query's |
| 160 » » // Filter syntax, but the order information may indicate to use
a descending |
| 161 » » // index column for it. If that's the case, then we must invert,
swap and |
| 162 » » // increment the inequality endpoints. |
| 163 » » // |
| 164 » » // Invert so that the desired numbers are represented correctly
in the index. |
| 165 » » // Swap so that our iterators still go from >= start to < end. |
| 166 » » // Increment so that >= and < get correctly bounded (since the i
terator is |
| 167 » » // still using natrual bytes ordering) |
| 168 » » if ret.suffixFormat[0].Descending { |
| 169 » » » hi, lo := []byte(nil), []byte(nil) |
| 170 » » » if len(startD) > 0 { |
| 171 » » » » lo = increment(invert(startD)) |
| 172 » » » } |
| 173 » » » if len(endD) > 0 { |
| 174 » » » » hi = increment(invert(endD)) |
| 175 » » » } |
| 176 » » » endD, startD = lo, hi |
| 239 } | 177 } |
| 240 } | 178 } |
| 241 | 179 |
| 242 ret := &reducedQuery{ | |
| 243 ns: q.ns, | |
| 244 kind: q.kind, | |
| 245 eqFilters: q.eqFilters, | |
| 246 suffixFormat: q.order, | |
| 247 } | |
| 248 | |
| 249 // if len(q.suffixFormat) > 0, queryImpl already enforces that the first
order | |
| 250 // is the same as the inequality. Otherwise we need to add it. | |
| 251 if len(ret.suffixFormat) == 0 && q.ineqFilter.prop != "" { | |
| 252 ret.suffixFormat = []ds.IndexColumn{{Property: q.ineqFilter.prop
}} | |
| 253 } | |
| 254 | |
| 255 // The inequality is specified in natural (ascending) order in the query
's | |
| 256 // Filter syntax, but the order information may indicate to use a descen
ding | |
| 257 // index column for it. If that's the case, then we must invert, swap an
d | |
| 258 // increment the inequality endpoints. | |
| 259 // | |
| 260 // Invert so that the desired numbers are represented correctly in the i
ndex. | |
| 261 // Swap so that our iterators still go from >= start to < end. | |
| 262 // Increment so that >= and < get correctly bounded (since the iterator
is | |
| 263 // still using natrual bytes ordering) | |
| 264 if q.ineqFilter.prop != "" && ret.suffixFormat[0].Direction == ds.DESCEN
DING { | |
| 265 hi, lo := []byte(nil), []byte(nil) | |
| 266 if len(q.ineqFilter.end) > 0 { | |
| 267 hi = increment(invert(q.ineqFilter.end)) | |
| 268 } | |
| 269 if len(q.ineqFilter.start) > 0 { | |
| 270 lo = increment(invert(q.ineqFilter.start)) | |
| 271 } | |
| 272 q.ineqFilter.end, q.ineqFilter.start = lo, hi | |
| 273 } | |
| 274 | |
| 275 // Add any projection columns not mentioned in the user-defined order as | |
| 276 // ASCENDING orders. Technically we could be smart and automatically use | |
| 277 // a DESCENDING ordered index, if it fit, but the logic gets insane, sin
ce all | |
| 278 // suffixes of all used indexes need to be PRECISELY equal (and so you'd
have | |
| 279 // to hunt/invalidate/something to find the combination of indexes that
are | |
| 280 // compatible with each other as well as the query). If you want to use | |
| 281 // a DESCENDING column, just add it to the user sort order, and this loo
p will | |
| 282 // not synthesize a new suffix entry for it. | |
| 283 // | |
| 284 // NOTE: if you want to use an index that sorts by -__key__, you MUST | |
| 285 // include all of the projected fields for that index in the order expli
citly. | |
| 286 // Otherwise the generated suffixFormat will be wacky. So: | |
| 287 // Query("Foo").Project("A", "B").Order("A").Order("-__key__") | |
| 288 // | |
| 289 // will turn into a suffixFormat of: | |
| 290 // A, ASCENDING | |
| 291 // __key__, DESCENDING | |
| 292 // B, ASCENDING | |
| 293 // __key__, ASCENDING | |
| 294 // | |
| 295 // To prevent this, your query should have another Order("B") clause bef
ore | |
| 296 // the -__key__ clause. | |
| 297 originalStop := len(ret.suffixFormat) | |
| 298 for _, p := range q.project { | |
| 299 needAdd := true | |
| 300 // originalStop prevents this loop from getting longer every tim
e we add | |
| 301 // a projected property. | |
| 302 for _, col := range ret.suffixFormat[:originalStop] { | |
| 303 if col.Property == p { | |
| 304 needAdd = false | |
| 305 break | |
| 306 } | |
| 307 } | |
| 308 if needAdd { | |
| 309 ret.suffixFormat = append(ret.suffixFormat, ds.IndexColu
mn{Property: p}) | |
| 310 } | |
| 311 } | |
| 312 | |
| 313 // If the suffix format ends with __key__ already (e.g. .Order("__key__"
)), | |
| 314 // then we're good to go. Otherwise we need to add it as the last bit of
the | |
| 315 // suffix, since all indexes implicitly have it as the last column. | |
| 316 if len(ret.suffixFormat) == 0 || ret.suffixFormat[len(ret.suffixFormat)-
1].Property != "__key__" { | |
| 317 ret.suffixFormat = append(ret.suffixFormat, ds.IndexColumn{Prope
rty: "__key__"}) | |
| 318 } | |
| 319 | |
| 320 // Now we check the start and end cursors. | 180 // Now we check the start and end cursors. |
| 321 // | 181 // |
| 322 // Cursors are composed of a list of IndexColumns at the beginning, foll
owed | 182 // Cursors are composed of a list of IndexColumns at the beginning, foll
owed |
| 323 // by the raw bytes to use for the suffix. The cursor is only valid if a
ll of | 183 // by the raw bytes to use for the suffix. The cursor is only valid if a
ll of |
| 324 // its IndexColumns match our proposed suffixFormat, as calculated above
. | 184 // its IndexColumns match our proposed suffixFormat, as calculated above
. |
| 325 » ret.start = q.ineqFilter.start | 185 » // |
| 326 » if q.startCursor != nil { | 186 » // Cursors are mutually exclusive with the start/end we picked up from t
he |
| 327 » » if !sortOrdersEqual(q.startCursorColumns, ret.suffixFormat) { | 187 » // inequality. In a well formed query, they indicate a subset of results |
| 328 » » » return nil, errors.New("gae/memory: start cursor is inva
lid for this query.") | 188 » // bounded by the inequality. Technically if the start cursor is not >=
the |
| 189 » // low bound, or the end cursor is < the high bound, it's an error, but
for |
| 190 » // simplicity we just cap to the narrowest intersection of the inequalit
y and |
| 191 » // cursors. |
| 192 » ret.start = startD |
| 193 » ret.end = endD |
| 194 » if start, end := fq.Bounds(); start != nil || end != nil { |
| 195 » » if start != nil { |
| 196 » » » if c, ok := start.(queryCursor); ok { |
| 197 » » » » startCols, startD, err := c.decode() |
| 198 » » » » if err != nil { |
| 199 » » » » » return nil, err |
| 200 » » » » } |
| 201 |
| 202 » » » » if !sortOrdersEqual(startCols, ret.suffixFormat)
{ |
| 203 » » » » » return nil, errors.New("gae/memory: star
t cursor is invalid for this query") |
| 204 » » » » } |
| 205 » » » » if ret.start == nil || bytes.Compare(ret.start,
startD) < 0 { |
| 206 » » » » » ret.start = startD |
| 207 » » » » } |
| 208 » » » } else { |
| 209 » » » » return nil, errors.New("gae/memory: bad cursor t
ype") |
| 210 » » » } |
| 329 } | 211 } |
| 330 if ret.start == nil || bytes.Compare(ret.start, q.startCursor) <
0 { | |
| 331 ret.start = q.startCursor | |
| 332 } | |
| 333 } | |
| 334 | 212 |
| 335 » ret.end = q.ineqFilter.end | 213 » » if end != nil { |
| 336 » if q.endCursor != nil { | 214 » » » if c, ok := end.(queryCursor); ok { |
| 337 » » if !sortOrdersEqual(q.endCursorColumns, ret.suffixFormat) { | 215 » » » » endCols, endD, err := c.decode() |
| 338 » » » return nil, errors.New("gae/memory: end cursor is invali
d for this query.") | 216 » » » » if err != nil { |
| 339 » » } | 217 » » » » » return nil, err |
| 340 » » if ret.end == nil || bytes.Compare(q.endCursor, ret.end) < 0 { | 218 » » » » } |
| 341 » » » ret.end = q.endCursor | 219 |
| 220 » » » » if !sortOrdersEqual(endCols, ret.suffixFormat) { |
| 221 » » » » » return nil, errors.New("gae/memory: end
cursor is invalid for this query") |
| 222 » » » » } |
| 223 » » » » if ret.end == nil || bytes.Compare(endD, ret.end
) < 0 { |
| 224 » » » » » ret.end = endD |
| 225 » » » » } |
| 226 » » » } else { |
| 227 » » » » return nil, errors.New("gae/memory: bad cursor t
ype") |
| 228 » » » } |
| 342 } | 229 } |
| 343 } | 230 } |
| 344 | 231 |
| 345 // Finally, verify that we could even /potentially/ do work. If we have | 232 // Finally, verify that we could even /potentially/ do work. If we have |
| 346 // overlapping range ends, then we don't have anything to do. | 233 // overlapping range ends, then we don't have anything to do. |
| 347 if ret.end != nil && bytes.Compare(ret.start, ret.end) >= 0 { | 234 if ret.end != nil && bytes.Compare(ret.start, ret.end) >= 0 { |
| 348 » » return nil, errQueryDone | 235 » » return nil, ds.ErrNullQuery |
| 349 } | 236 } |
| 350 | 237 |
| 351 ret.numCols = len(ret.suffixFormat) | 238 ret.numCols = len(ret.suffixFormat) |
| 352 for prop, vals := range ret.eqFilters { | 239 for prop, vals := range ret.eqFilters { |
| 353 if len(ret.suffixFormat) == 1 && prop == "__ancestor__" { | 240 if len(ret.suffixFormat) == 1 && prop == "__ancestor__" { |
| 354 continue | 241 continue |
| 355 } | 242 } |
| 356 ret.numCols += vals.Len() | 243 ret.numCols += vals.Len() |
| 357 } | 244 } |
| 358 | 245 |
| 359 return ret, nil | 246 return ret, nil |
| 360 } | 247 } |
| 361 | |
| 362 func (q *queryImpl) numComponents() int { | |
| 363 numComponents := len(q.order) | |
| 364 if q.ineqFilter.prop != "" { | |
| 365 if q.ineqFilter.start != nil { | |
| 366 numComponents++ | |
| 367 } | |
| 368 if q.ineqFilter.end != nil { | |
| 369 numComponents++ | |
| 370 } | |
| 371 } | |
| 372 for _, v := range q.eqFilters { | |
| 373 numComponents += v.Len() | |
| 374 } | |
| 375 return numComponents | |
| 376 } | |
| 377 | |
| 378 // checkMutateClone sees if the query has an error. If not, it clones the query, | |
| 379 // and assigns the output of `check` to the query error slot. If check returns | |
| 380 // nil, it calls `mutate` on the cloned query. The (possibly new) query is then | |
| 381 // returned. | |
| 382 func (q *queryImpl) checkMutateClone(check func() error, mutate func(*queryImpl)
) *queryImpl { | |
| 383 if q.err != nil { | |
| 384 return q | |
| 385 } | |
| 386 nq := *q | |
| 387 nq.eqFilters = make(map[string]stringset.Set, len(q.eqFilters)) | |
| 388 for prop, vals := range q.eqFilters { | |
| 389 nq.eqFilters[prop] = vals.Dup() | |
| 390 } | |
| 391 nq.order = make([]ds.IndexColumn, len(q.order)) | |
| 392 copy(nq.order, q.order) | |
| 393 nq.project = make([]string, len(q.project)) | |
| 394 copy(nq.project, q.project) | |
| 395 if check != nil { | |
| 396 nq.err = check() | |
| 397 } | |
| 398 if nq.err == nil { | |
| 399 mutate(&nq) | |
| 400 } | |
| 401 return &nq | |
| 402 } | |
| 403 | |
| 404 func (q *queryImpl) Ancestor(k ds.Key) ds.Query { | |
| 405 return q.checkMutateClone( | |
| 406 func() error { | |
| 407 if k == nil { | |
| 408 // SDK has an explicit nil-check | |
| 409 return errors.New("datastore: nil query ancestor
") | |
| 410 } | |
| 411 if k.Namespace() != q.ns { | |
| 412 return fmt.Errorf("bad namespace: %q (expected %
q)", k.Namespace(), q.ns) | |
| 413 } | |
| 414 if !k.Valid(false, globalAppID, q.ns) { | |
| 415 // technically the SDK implementation does a Wei
rd Thing (tm) if both the | |
| 416 // stringID and intID are set on a key; it only
serializes the stringID in | |
| 417 // the proto. This means that if you set the Anc
estor to an invalid key, | |
| 418 // you'll never actually hear about it. Instead
of doing that insanity, we | |
| 419 // just swap to an error here. | |
| 420 return ds.ErrInvalidKey | |
| 421 } | |
| 422 if q.eqFilters["__ancestor__"] != nil { | |
| 423 return errors.New("cannot have more than one anc
estor") | |
| 424 } | |
| 425 return nil | |
| 426 }, | |
| 427 func(q *queryImpl) { | |
| 428 q.addEqFilt("__ancestor__", ds.MkProperty(k)) | |
| 429 }) | |
| 430 } | |
| 431 | |
| 432 func (q *queryImpl) Distinct() ds.Query { | |
| 433 return q.checkMutateClone(nil, func(q *queryImpl) { | |
| 434 q.distinct = true | |
| 435 }) | |
| 436 } | |
| 437 | |
| 438 func (q *queryImpl) addEqFilt(prop string, p ds.Property) { | |
| 439 binVal := string(serialize.ToBytes(p)) | |
| 440 if cur, ok := q.eqFilters[prop]; !ok { | |
| 441 s := stringset.New(1) | |
| 442 s.Add(binVal) | |
| 443 q.eqFilters[prop] = s | |
| 444 } else { | |
| 445 cur.Add(binVal) | |
| 446 } | |
| 447 } | |
| 448 | |
| 449 func (q *queryImpl) Filter(fStr string, val interface{}) ds.Query { | |
| 450 prop := "" | |
| 451 op := qInvalid | |
| 452 p := ds.Property{} | |
| 453 return q.checkMutateClone( | |
| 454 func() error { | |
| 455 var err error | |
| 456 prop, op, err = parseFilter(fStr) | |
| 457 if err != nil { | |
| 458 return err | |
| 459 } | |
| 460 | |
| 461 if q.kind == "" && prop != "__key__" { | |
| 462 // https://cloud.google.com/appengine/docs/go/da
tastore/queries#Go_Kindless_queries | |
| 463 return fmt.Errorf( | |
| 464 "kindless queries can only filter on __k
ey__, got %q", fStr) | |
| 465 } | |
| 466 | |
| 467 err = p.SetValue(val, ds.ShouldIndex) | |
| 468 if err != nil { | |
| 469 return err | |
| 470 } | |
| 471 | |
| 472 if p.Type() == ds.PTKey { | |
| 473 if !p.Value().(ds.Key).Valid(false, globalAppID,
q.ns) { | |
| 474 return ds.ErrInvalidKey | |
| 475 } | |
| 476 } | |
| 477 | |
| 478 if prop == "__key__" { | |
| 479 if op == qEqual { | |
| 480 return fmt.Errorf( | |
| 481 "query equality filter on __key_
_ is silly: %q", fStr) | |
| 482 } | |
| 483 if p.Type() != ds.PTKey { | |
| 484 return fmt.Errorf("__key__ filter value
is not a key: %T", val) | |
| 485 } | |
| 486 } else if strings.HasPrefix(prop, "__") && strings.HasSu
ffix(prop, "__") { | |
| 487 return fmt.Errorf("filter on reserved property:
%q", prop) | |
| 488 } | |
| 489 | |
| 490 if op != qEqual { | |
| 491 if q.ineqFilter.prop != "" && q.ineqFilter.prop
!= prop { | |
| 492 return fmt.Errorf( | |
| 493 "inequality filters on multiple
properties: %q and %q", | |
| 494 q.ineqFilter.prop, prop) | |
| 495 } | |
| 496 if len(q.order) > 0 && q.order[0].Property != pr
op { | |
| 497 return fmt.Errorf( | |
| 498 "first sort order must match ine
quality filter: %q v %q", | |
| 499 q.order[0].Property, prop) | |
| 500 } | |
| 501 } else { | |
| 502 for _, p := range q.project { | |
| 503 if p == prop { | |
| 504 return fmt.Errorf( | |
| 505 "cannot project on field
which is used in an equality filter: %q", | |
| 506 prop) | |
| 507 } | |
| 508 } | |
| 509 } | |
| 510 return err | |
| 511 }, | |
| 512 func(q *queryImpl) { | |
| 513 if op == qEqual { | |
| 514 // add it to eq filters | |
| 515 q.addEqFilt(prop, p) | |
| 516 | |
| 517 // remove it from sort orders. | |
| 518 // https://cloud.google.com/appengine/docs/go/da
tastore/queries#sort_orders_are_ignored_on_properties_with_equality_filters | |
| 519 toRm := -1 | |
| 520 for i, o := range q.order { | |
| 521 if o.Property == prop { | |
| 522 toRm = i | |
| 523 break | |
| 524 } | |
| 525 } | |
| 526 if toRm >= 0 { | |
| 527 q.order = append(q.order[:toRm], q.order
[toRm+1:]...) | |
| 528 } | |
| 529 } else { | |
| 530 q.ineqFilter.prop = prop | |
| 531 if q.ineqFilter.constrain(op, serialize.ToBytes(
p)) { | |
| 532 q.err = errQueryDone | |
| 533 } | |
| 534 } | |
| 535 }) | |
| 536 } | |
| 537 | |
| 538 func (q *queryImpl) Order(prop string) ds.Query { | |
| 539 col := ds.IndexColumn{} | |
| 540 return q.checkMutateClone( | |
| 541 func() error { | |
| 542 // check that first order == first inequality. | |
| 543 // if order is an equality already, ignore it | |
| 544 col.Property = strings.TrimSpace(prop) | |
| 545 if strings.HasPrefix(prop, "-") { | |
| 546 col.Direction = ds.DESCENDING | |
| 547 col.Property = strings.TrimSpace(prop[1:]) | |
| 548 } else if strings.HasPrefix(prop, "+") { | |
| 549 return fmt.Errorf("datastore: invalid order: %q"
, prop) | |
| 550 } | |
| 551 if len(col.Property) == 0 { | |
| 552 return errors.New("datastore: empty order") | |
| 553 } | |
| 554 if len(q.order) == 0 && q.ineqFilter.prop != "" && q.ine
qFilter.prop != col.Property { | |
| 555 return fmt.Errorf( | |
| 556 "first sort order must match inequality
filter: %q v %q", | |
| 557 prop, q.ineqFilter.prop) | |
| 558 } | |
| 559 if q.kind == "" && (col.Property != "__key__" || col.Dir
ection != ds.ASCENDING) { | |
| 560 return fmt.Errorf("invalid order for kindless qu
ery: %#v", col) | |
| 561 } | |
| 562 return nil | |
| 563 }, | |
| 564 func(q *queryImpl) { | |
| 565 if _, ok := q.eqFilters[col.Property]; ok { | |
| 566 // skip it if it's an equality filter | |
| 567 // https://cloud.google.com/appengine/docs/go/da
tastore/queries#sort_orders_are_ignored_on_properties_with_equality_filters | |
| 568 return | |
| 569 } | |
| 570 for _, order := range q.order { | |
| 571 if order.Property == col.Property { | |
| 572 // can't sort by the same order twice | |
| 573 return | |
| 574 } | |
| 575 } | |
| 576 q.order = append(q.order, col) | |
| 577 }) | |
| 578 } | |
| 579 | |
| 580 func (q *queryImpl) Project(fieldName ...string) ds.Query { | |
| 581 return q.checkMutateClone( | |
| 582 func() error { | |
| 583 if q.keysOnly { | |
| 584 return errors.New("cannot project a keysOnly que
ry") | |
| 585 } | |
| 586 dupCheck := stringset.New(len(fieldName) + len(q.project
)) | |
| 587 for _, f := range fieldName { | |
| 588 if !dupCheck.Add(f) { | |
| 589 return fmt.Errorf("cannot project on the
same field twice: %q", f) | |
| 590 } | |
| 591 if f == "" { | |
| 592 return errors.New("cannot project on an
empty field name") | |
| 593 } | |
| 594 if f == "__key__" { | |
| 595 return fmt.Errorf("cannot project on __k
ey__") | |
| 596 } | |
| 597 if _, ok := q.eqFilters[f]; ok { | |
| 598 return fmt.Errorf( | |
| 599 "cannot project on field which i
s used in an equality filter: %q", f) | |
| 600 } | |
| 601 for _, p := range q.project { | |
| 602 if p == f { | |
| 603 return fmt.Errorf("cannot projec
t on the same field twice: %q", f) | |
| 604 } | |
| 605 } | |
| 606 } | |
| 607 return nil | |
| 608 }, | |
| 609 func(q *queryImpl) { | |
| 610 q.project = append(q.project, fieldName...) | |
| 611 }) | |
| 612 } | |
| 613 | |
| 614 func (q *queryImpl) KeysOnly() ds.Query { | |
| 615 return q.checkMutateClone( | |
| 616 func() error { | |
| 617 if len(q.project) != 0 { | |
| 618 return errors.New("cannot project a keysOnly que
ry") | |
| 619 } | |
| 620 return nil | |
| 621 }, | |
| 622 func(q *queryImpl) { | |
| 623 q.keysOnly = true | |
| 624 }) | |
| 625 } | |
| 626 | |
| 627 func (q *queryImpl) Limit(limit int) ds.Query { | |
| 628 return q.checkMutateClone( | |
| 629 func() error { | |
| 630 // nonsensically... ANY negative value means 'unlimited'
. *shakes head* | |
| 631 if limit < math.MinInt32 || limit > math.MaxInt32 { | |
| 632 return errors.New("datastore: query limit overfl
ow") | |
| 633 } | |
| 634 return nil | |
| 635 }, | |
| 636 func(q *queryImpl) { | |
| 637 q.limitSet = true | |
| 638 q.limit = int32(limit) | |
| 639 }) | |
| 640 } | |
| 641 | |
| 642 func (q *queryImpl) Offset(offset int) ds.Query { | |
| 643 return q.checkMutateClone( | |
| 644 func() error { | |
| 645 if offset < 0 { | |
| 646 return errors.New("datastore: negative query off
set") | |
| 647 } | |
| 648 if offset > math.MaxInt32 { | |
| 649 return errors.New("datastore: query offset overf
low") | |
| 650 } | |
| 651 return nil | |
| 652 }, | |
| 653 func(q *queryImpl) { | |
| 654 q.offset = int32(offset) | |
| 655 }) | |
| 656 } | |
| 657 | |
| 658 func queryCursorCheck(ns, flavor string, current []byte, newCursor ds.Cursor) ([
]ds.IndexColumn, []byte, error) { | |
| 659 if current != nil { | |
| 660 return nil, nil, fmt.Errorf("%s cursor is multiply defined", fla
vor) | |
| 661 } | |
| 662 curs, ok := newCursor.(queryCursor) | |
| 663 if !ok { | |
| 664 return nil, nil, fmt.Errorf("%s cursor is unknown type: %T", fla
vor, curs) | |
| 665 } | |
| 666 return curs.decode() | |
| 667 } | |
| 668 | |
| 669 func (q *queryImpl) Start(c ds.Cursor) ds.Query { | |
| 670 cols := []ds.IndexColumn(nil) | |
| 671 curs := []byte(nil) | |
| 672 return q.checkMutateClone( | |
| 673 func() (err error) { | |
| 674 cols, curs, err = queryCursorCheck(q.ns, "start", q.star
tCursor, c) | |
| 675 return | |
| 676 }, | |
| 677 func(q *queryImpl) { | |
| 678 q.startCursorColumns = cols | |
| 679 q.startCursor = curs | |
| 680 }) | |
| 681 } | |
| 682 | |
| 683 func (q *queryImpl) End(c ds.Cursor) ds.Query { | |
| 684 cols := []ds.IndexColumn(nil) | |
| 685 curs := queryCursor(nil) | |
| 686 return q.checkMutateClone( | |
| 687 func() (err error) { | |
| 688 cols, curs, err = queryCursorCheck(q.ns, "end", q.endCur
sor, c) | |
| 689 return | |
| 690 }, | |
| 691 func(q *queryImpl) { | |
| 692 q.endCursorColumns = cols | |
| 693 q.endCursor = curs | |
| 694 }) | |
| 695 } | |
| 696 | |
| 697 func (q *queryImpl) EventualConsistency() ds.Query { | |
| 698 return q.checkMutateClone( | |
| 699 nil, func(q *queryImpl) { | |
| 700 q.eventualConsistency = true | |
| 701 }) | |
| 702 } | |
| OLD | NEW |