Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: service/datastore/finalized_query.go

Issue 1355783002: Refactor keys and queries in datastore service and implementation. (Closed) Base URL: https://github.com/luci/gae.git@master
Patch Set: fix comments Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 {
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))
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 = ">"
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 = "<"
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 {
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, ", "))
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 anc := Property{}
184 if len(q.eqFilts) > 0 {
185 eqProps := make([]string, 0, len(q.eqFilts))
186 for k, v := range q.eqFilts {
187 if k == "__ancestor__" {
188 anc = v[0]
189 continue
190 }
191 eqProps = append(eqProps, k)
192 }
193 sort.Strings(eqProps)
194 for _, k := range eqProps {
195 vals := q.eqFilts[k]
196 k = gqlQuoteName(k)
197 for _, v := range vals {
198 if v.Type() == PTNull {
199 filts = append(filts, fmt.Sprintf("%s IS NULL", k))
200 } else {
201 filts = append(filts, fmt.Sprintf("%s = %s", k, v.GQL()))
202 }
203 }
204 }
205 }
206 if q.ineqFiltProp != "" {
207 for _, f := range [](func() (p, op string, v Property)){q.IneqFi lterLow, q.IneqFilterHigh} {
208 prop, op, v := f()
209 if prop != "" {
210 filts = append(filts, fmt.Sprintf("%s %s %s", gq lQuoteName(prop), op, v.GQL()))
211 }
212 }
213 }
214 if anc.propType != PTNull {
215 filts = append(filts, fmt.Sprintf("__key__ HAS ANCESTOR %s", anc .GQL()))
216 }
217 if len(filts) > 0 {
218 fmt.Fprintf(&ret, " WHERE %s", strings.Join(filts, " AND "))
219 }
220
221 if len(q.orders) > 0 {
222 orders := make([]string, len(q.orders))
223 for i, col := range q.orders {
224 orders[i] = col.GQL()
225 }
226 fmt.Fprintf(&ret, " ORDER BY %s", strings.Join(orders, ", "))
227 }
228
229 if q.limit != nil {
230 fmt.Fprintf(&ret, " LIMIT %d", *q.limit)
231 }
232 if q.offset != nil {
233 fmt.Fprintf(&ret, " OFFSET %d", *q.offset)
234 }
235
236 return ret.String()
237 }
238
239 func (q *FinalizedQuery) String() string {
240 // TODO(riannucci): make a more compact go-like representation here.
241 return q.GQL()
242 }
243
244 func (q *FinalizedQuery) Valid(aid, ns string) error {
245 anc := q.Ancestor()
246 if anc != nil && (!anc.Valid(false, aid, ns) || anc.Incomplete()) {
247 return ErrInvalidKey
248 }
249
250 if q.ineqFiltProp == "__key__" {
251 if q.ineqFiltLowSet && !q.ineqFiltLow.Value().(*Key).Valid(false , aid, ns) {
252 return ErrInvalidKey
253 }
254 if q.ineqFiltHighSet && !q.ineqFiltHigh.Value().(*Key).Valid(fal se, aid, ns) {
255 return ErrInvalidKey
256 }
257 }
258 return nil
259 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698