Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package datastore | |
| 6 | |
| 7 import ( | |
| 8 "bytes" | |
| 9 "fmt" | |
| 10 "sort" | |
| 11 "strings" | |
| 12 ) | |
| 13 | |
| 14 type FinalizedQuery struct { | |
| 15 original *Query | |
| 16 | |
| 17 kind string | |
| 18 eventuallyConsistent bool | |
| 19 distinct bool | |
| 20 keysOnly bool | |
| 21 | |
| 22 limit *int32 | |
| 23 offset *int32 | |
| 24 | |
| 25 start Cursor | |
| 26 end Cursor | |
| 27 | |
| 28 project []string | |
| 29 orders []IndexColumn | |
| 30 | |
| 31 eqFilts map[string]PropertySlice | |
| 32 | |
| 33 ineqFiltProp string | |
| 34 ineqFiltLow Property | |
| 35 ineqFiltLowIncl bool | |
| 36 ineqFiltLowSet bool | |
| 37 ineqFiltHigh Property | |
| 38 ineqFiltHighIncl bool | |
| 39 ineqFiltHighSet bool | |
| 40 } | |
| 41 | |
| 42 func (q *FinalizedQuery) Original() *Query { | |
|
iannucci
2015/09/18 04:31:53
yeah, I know I need docstrings on all of these.
iannucci
2015/09/18 04:31:53
yeah, I know I need docstrings on all of these.
dnj
2015/09/18 16:47:57
yeah, I know you know you need docstrings on all o
| |
| 43 return q.original | |
| 44 } | |
| 45 | |
| 46 func (q *FinalizedQuery) Kind() string { | |
| 47 return q.kind | |
| 48 } | |
| 49 | |
| 50 func (q *FinalizedQuery) EventuallyConsistent() bool { | |
| 51 return q.eventuallyConsistent | |
| 52 } | |
| 53 | |
| 54 func (q *FinalizedQuery) Project() []string { | |
| 55 ret := make([]string, len(q.project)) | |
|
dnj
2015/09/18 16:47:57
Maybe an allocation-avoiding shortcut here:
if len
iannucci
2015/09/18 22:25:48
done
dnj
2015/09/18 22:45:12
I think you missed this.
| |
| 56 copy(ret, q.project) | |
| 57 return ret | |
| 58 } | |
| 59 | |
| 60 func (q *FinalizedQuery) Distinct() bool { | |
| 61 return q.distinct | |
| 62 } | |
| 63 | |
| 64 func (q *FinalizedQuery) KeysOnly() bool { | |
| 65 return q.keysOnly | |
| 66 } | |
| 67 | |
| 68 func (q *FinalizedQuery) Limit() (int32, bool) { | |
| 69 if q.limit != nil { | |
| 70 return *q.limit, true | |
| 71 } | |
| 72 return 0, false | |
| 73 } | |
| 74 | |
| 75 func (q *FinalizedQuery) Offset() (int32, bool) { | |
| 76 if q.offset != nil { | |
| 77 return *q.offset, true | |
| 78 } | |
| 79 return 0, false | |
| 80 } | |
| 81 | |
| 82 func (q *FinalizedQuery) Orders() []IndexColumn { | |
| 83 ret := make([]IndexColumn, len(q.orders)) | |
| 84 copy(ret, q.orders) | |
| 85 return ret | |
| 86 } | |
| 87 | |
| 88 func (q *FinalizedQuery) Bounds() (start, end Cursor) { | |
| 89 return q.start, q.end | |
| 90 } | |
| 91 | |
| 92 func (q *FinalizedQuery) Ancestor() *Key { | |
| 93 if anc, ok := q.eqFilts["__ancestor__"]; ok { | |
| 94 return anc[0].Value().(*Key) | |
| 95 } | |
| 96 return nil | |
| 97 } | |
| 98 | |
| 99 func (q *FinalizedQuery) EqFilters() map[string]PropertySlice { | |
| 100 ret := make(map[string]PropertySlice, len(q.eqFilts)) | |
| 101 for k, v := range q.eqFilts { | |
| 102 newV := make(PropertySlice, len(v)) | |
| 103 copy(newV, v) | |
| 104 ret[k] = newV | |
| 105 } | |
| 106 return ret | |
| 107 } | |
| 108 | |
| 109 func (q *FinalizedQuery) IneqFilterProp() string { | |
| 110 return q.ineqFiltProp | |
| 111 } | |
| 112 | |
| 113 func (q *FinalizedQuery) IneqFilterLow() (field, op string, val Property) { | |
| 114 if q.ineqFiltLowSet { | |
| 115 field = q.ineqFiltProp | |
| 116 val = q.ineqFiltLow | |
| 117 op = ">" | |
|
dnj
2015/09/18 16:47:57
Since "op" is a return value, you can avoid the do
iannucci
2015/09/18 22:25:48
¯\_(ツ)_/¯
| |
| 118 if q.ineqFiltLowIncl { | |
| 119 op = ">=" | |
| 120 } | |
| 121 } | |
| 122 return | |
| 123 } | |
| 124 | |
| 125 func (q *FinalizedQuery) IneqFilterHigh() (field, op string, val Property) { | |
| 126 if q.ineqFiltHighSet { | |
| 127 field = q.ineqFiltProp | |
| 128 val = q.ineqFiltHigh | |
| 129 op = "<" | |
|
dnj
2015/09/18 16:47:57
Since "op" is a return value, you can avoid the do
| |
| 130 if q.ineqFiltHighIncl { | |
| 131 op = "<=" | |
| 132 } | |
| 133 } | |
| 134 return | |
| 135 } | |
| 136 | |
| 137 var escaper = strings.NewReplacer( | |
| 138 "\\%", `\%`, | |
| 139 "\\_", `\_`, | |
| 140 "\\", `\\`, | |
| 141 "\x00", `\0`, | |
| 142 "\b", `\b`, | |
| 143 "\n", `\n`, | |
| 144 "\r", `\r`, | |
| 145 "\t", `\t`, | |
| 146 "\x1A", `\Z`, | |
| 147 "'", `\'`, | |
| 148 "\"", `\"`, | |
| 149 "`", "\\`", | |
| 150 ) | |
| 151 | |
| 152 func gqlQuoteName(s string) string { | |
| 153 return fmt.Sprintf("`%s`", escaper.Replace(s)) | |
| 154 } | |
| 155 | |
| 156 func gqlQuoteString(s string) string { | |
| 157 return fmt.Sprintf(`"%s"`, escaper.Replace(s)) | |
| 158 } | |
| 159 | |
| 160 func (q *FinalizedQuery) GQL() string { | |
|
dnj
2015/09/18 16:47:57
Make sure you mention which dialect of GQL you're
| |
| 161 ret := bytes.Buffer{} | |
| 162 | |
| 163 ret.WriteString("SELECT") | |
| 164 if len(q.project) != 0 { | |
| 165 if q.distinct { | |
| 166 ret.WriteString(" DISTINCT") | |
| 167 } | |
| 168 proj := make([]string, len(q.project)) | |
| 169 for i, p := range q.project { | |
| 170 proj[i] = gqlQuoteName(p) | |
| 171 } | |
| 172 ret.WriteString(" ") | |
| 173 ret.WriteString(strings.Join(proj, ", ")) | |
|
dnj
2015/09/18 16:47:57
nit: Since we're already leaning heavily on the Bu
iannucci
2015/09/18 22:25:48
if there are a bunch of projections, that's a lot
| |
| 174 } else { | |
| 175 ret.WriteString(" *") | |
| 176 } | |
| 177 | |
| 178 if q.kind != "" { | |
| 179 fmt.Fprintf(&ret, " FROM %s", gqlQuoteName(q.kind)) | |
| 180 } | |
| 181 | |
| 182 filts := []string(nil) | |
| 183 if len(q.eqFilts) > 0 { | |
| 184 eqProps := make([]string, 0, len(q.eqFilts)) | |
| 185 for k := range q.eqFilts { | |
| 186 if k == "__ancestor__" { | |
|
dnj
2015/09/18 16:47:57
Retain this for later so you don't have to double-
iannucci
2015/09/18 22:25:48
done
| |
| 187 continue | |
| 188 } | |
| 189 eqProps = append(eqProps, k) | |
| 190 } | |
| 191 sort.Strings(eqProps) | |
| 192 for _, k := range eqProps { | |
| 193 vals := q.eqFilts[k] | |
| 194 k = gqlQuoteName(k) | |
| 195 for _, v := range vals { | |
| 196 if v.Type() == PTNull { | |
| 197 filts = append(filts, fmt.Sprintf("%s IS NULL", k)) | |
| 198 } else { | |
| 199 filts = append(filts, fmt.Sprintf("%s = %s", k, v.GQL())) | |
| 200 } | |
| 201 } | |
| 202 } | |
| 203 } | |
| 204 if q.ineqFiltProp != "" { | |
| 205 for _, f := range [](func() (p, op string, v Property)){q.IneqFi lterLow, q.IneqFilterHigh} { | |
| 206 prop, op, v := f() | |
| 207 if prop != "" { | |
| 208 filts = append(filts, fmt.Sprintf("%s %s %s", gq lQuoteName(prop), op, v.GQL())) | |
| 209 } | |
| 210 } | |
| 211 } | |
| 212 if anc, ok := q.eqFilts["__ancestor__"]; ok { | |
|
dnj
2015/09/18 16:47:57
Use retained value here.
| |
| 213 filts = append(filts, fmt.Sprintf("__key__ HAS ANCESTOR %s", anc [0].GQL())) | |
| 214 } | |
| 215 if len(filts) > 0 { | |
| 216 fmt.Fprintf(&ret, " WHERE %s", strings.Join(filts, " AND ")) | |
| 217 } | |
| 218 | |
| 219 if len(q.orders) > 0 { | |
| 220 orders := make([]string, len(q.orders)) | |
| 221 for i, col := range q.orders { | |
| 222 orders[i] = col.GQL() | |
| 223 } | |
| 224 fmt.Fprintf(&ret, " ORDER BY %s", strings.Join(orders, ", ")) | |
| 225 } | |
| 226 | |
| 227 if q.limit != nil { | |
| 228 fmt.Fprintf(&ret, " LIMIT %d", *q.limit) | |
| 229 } | |
| 230 if q.offset != nil { | |
| 231 fmt.Fprintf(&ret, " OFFSET %d", *q.offset) | |
| 232 } | |
| 233 | |
| 234 return ret.String() | |
| 235 } | |
| 236 | |
| 237 func (q *FinalizedQuery) String() string { | |
| 238 // TODO(riannucci): make a more compact go-like representation here. | |
| 239 return q.GQL() | |
| 240 } | |
| 241 | |
| 242 func (q *FinalizedQuery) Valid(aid, ns string) error { | |
| 243 anc := q.Ancestor() | |
| 244 if anc != nil && (!anc.Valid(false, aid, ns) || anc.Incomplete()) { | |
| 245 return ErrInvalidKey | |
| 246 } | |
| 247 | |
| 248 if q.ineqFiltProp == "__key__" { | |
| 249 if q.ineqFiltLowSet && !q.ineqFiltLow.Value().(*Key).Valid(false , aid, ns) { | |
| 250 return ErrInvalidKey | |
| 251 } | |
| 252 if q.ineqFiltHighSet && !q.ineqFiltHigh.Value().(*Key).Valid(fal se, aid, ns) { | |
| 253 return ErrInvalidKey | |
| 254 } | |
| 255 } | |
| 256 return nil | |
| 257 } | |
| OLD | NEW |