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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: service/datastore/finalized_query.go
diff --git a/service/datastore/finalized_query.go b/service/datastore/finalized_query.go
new file mode 100644
index 0000000000000000000000000000000000000000..1a7db9ad23f736aba1a074d60ac60d6e0113b6b7
--- /dev/null
+++ b/service/datastore/finalized_query.go
@@ -0,0 +1,259 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package datastore
+
+import (
+ "bytes"
+ "fmt"
+ "sort"
+ "strings"
+)
+
+type FinalizedQuery struct {
+ original *Query
+
+ kind string
+ eventuallyConsistent bool
+ distinct bool
+ keysOnly bool
+
+ limit *int32
+ offset *int32
+
+ start Cursor
+ end Cursor
+
+ project []string
+ orders []IndexColumn
+
+ eqFilts map[string]PropertySlice
+
+ ineqFiltProp string
+ ineqFiltLow Property
+ ineqFiltLowIncl bool
+ ineqFiltLowSet bool
+ ineqFiltHigh Property
+ ineqFiltHighIncl bool
+ ineqFiltHighSet bool
+}
+
+func (q *FinalizedQuery) Original() *Query {
+ return q.original
+}
+
+func (q *FinalizedQuery) Kind() string {
+ return q.kind
+}
+
+func (q *FinalizedQuery) EventuallyConsistent() bool {
+ return q.eventuallyConsistent
+}
+
+func (q *FinalizedQuery) Project() []string {
+ ret := make([]string, len(q.project))
+ copy(ret, q.project)
+ return ret
+}
+
+func (q *FinalizedQuery) Distinct() bool {
+ return q.distinct
+}
+
+func (q *FinalizedQuery) KeysOnly() bool {
+ return q.keysOnly
+}
+
+func (q *FinalizedQuery) Limit() (int32, bool) {
+ if q.limit != nil {
+ return *q.limit, true
+ }
+ return 0, false
+}
+
+func (q *FinalizedQuery) Offset() (int32, bool) {
+ if q.offset != nil {
+ return *q.offset, true
+ }
+ return 0, false
+}
+
+func (q *FinalizedQuery) Orders() []IndexColumn {
+ ret := make([]IndexColumn, len(q.orders))
+ copy(ret, q.orders)
+ return ret
+}
+
+func (q *FinalizedQuery) Bounds() (start, end Cursor) {
+ return q.start, q.end
+}
+
+func (q *FinalizedQuery) Ancestor() *Key {
+ if anc, ok := q.eqFilts["__ancestor__"]; ok {
+ return anc[0].Value().(*Key)
+ }
+ return nil
+}
+
+func (q *FinalizedQuery) EqFilters() map[string]PropertySlice {
+ ret := make(map[string]PropertySlice, len(q.eqFilts))
+ for k, v := range q.eqFilts {
+ newV := make(PropertySlice, len(v))
+ copy(newV, v)
+ ret[k] = newV
+ }
+ return ret
+}
+
+func (q *FinalizedQuery) IneqFilterProp() string {
+ return q.ineqFiltProp
+}
+
+func (q *FinalizedQuery) IneqFilterLow() (field, op string, val Property) {
+ if q.ineqFiltLowSet {
+ field = q.ineqFiltProp
+ val = q.ineqFiltLow
+ op = ">"
+ if q.ineqFiltLowIncl {
+ op = ">="
+ }
+ }
+ return
+}
+
+func (q *FinalizedQuery) IneqFilterHigh() (field, op string, val Property) {
+ if q.ineqFiltHighSet {
+ field = q.ineqFiltProp
+ val = q.ineqFiltHigh
+ op = "<"
+ if q.ineqFiltHighIncl {
+ op = "<="
+ }
+ }
+ return
+}
+
+var escaper = strings.NewReplacer(
+ "\\%", `\%`,
+ "\\_", `\_`,
+ "\\", `\\`,
+ "\x00", `\0`,
+ "\b", `\b`,
+ "\n", `\n`,
+ "\r", `\r`,
+ "\t", `\t`,
+ "\x1A", `\Z`,
+ "'", `\'`,
+ "\"", `\"`,
+ "`", "\\`",
+)
+
+func gqlQuoteName(s string) string {
+ return fmt.Sprintf("`%s`", escaper.Replace(s))
+}
+
+func gqlQuoteString(s string) string {
+ return fmt.Sprintf(`"%s"`, escaper.Replace(s))
+}
+
+func (q *FinalizedQuery) GQL() string {
+ ret := bytes.Buffer{}
+
+ ret.WriteString("SELECT")
+ if len(q.project) != 0 {
+ if q.distinct {
+ ret.WriteString(" DISTINCT")
+ }
+ proj := make([]string, len(q.project))
+ for i, p := range q.project {
+ proj[i] = gqlQuoteName(p)
+ }
+ ret.WriteString(" ")
+ ret.WriteString(strings.Join(proj, ", "))
+ } else {
+ ret.WriteString(" *")
+ }
+
+ if q.kind != "" {
+ fmt.Fprintf(&ret, " FROM %s", gqlQuoteName(q.kind))
+ }
+
+ filts := []string(nil)
+ anc := Property{}
+ if len(q.eqFilts) > 0 {
+ eqProps := make([]string, 0, len(q.eqFilts))
+ for k, v := range q.eqFilts {
+ if k == "__ancestor__" {
+ anc = v[0]
+ continue
+ }
+ eqProps = append(eqProps, k)
+ }
+ sort.Strings(eqProps)
+ for _, k := range eqProps {
+ vals := q.eqFilts[k]
+ k = gqlQuoteName(k)
+ for _, v := range vals {
+ if v.Type() == PTNull {
+ filts = append(filts, fmt.Sprintf("%s IS NULL", k))
+ } else {
+ filts = append(filts, fmt.Sprintf("%s = %s", k, v.GQL()))
+ }
+ }
+ }
+ }
+ if q.ineqFiltProp != "" {
+ for _, f := range [](func() (p, op string, v Property)){q.IneqFilterLow, q.IneqFilterHigh} {
+ prop, op, v := f()
+ if prop != "" {
+ filts = append(filts, fmt.Sprintf("%s %s %s", gqlQuoteName(prop), op, v.GQL()))
+ }
+ }
+ }
+ if anc.propType != PTNull {
+ filts = append(filts, fmt.Sprintf("__key__ HAS ANCESTOR %s", anc.GQL()))
+ }
+ if len(filts) > 0 {
+ fmt.Fprintf(&ret, " WHERE %s", strings.Join(filts, " AND "))
+ }
+
+ if len(q.orders) > 0 {
+ orders := make([]string, len(q.orders))
+ for i, col := range q.orders {
+ orders[i] = col.GQL()
+ }
+ fmt.Fprintf(&ret, " ORDER BY %s", strings.Join(orders, ", "))
+ }
+
+ if q.limit != nil {
+ fmt.Fprintf(&ret, " LIMIT %d", *q.limit)
+ }
+ if q.offset != nil {
+ fmt.Fprintf(&ret, " OFFSET %d", *q.offset)
+ }
+
+ return ret.String()
+}
+
+func (q *FinalizedQuery) String() string {
+ // TODO(riannucci): make a more compact go-like representation here.
+ return q.GQL()
+}
+
+func (q *FinalizedQuery) Valid(aid, ns string) error {
+ anc := q.Ancestor()
+ if anc != nil && (!anc.Valid(false, aid, ns) || anc.Incomplete()) {
+ return ErrInvalidKey
+ }
+
+ if q.ineqFiltProp == "__key__" {
+ if q.ineqFiltLowSet && !q.ineqFiltLow.Value().(*Key).Valid(false, aid, ns) {
+ return ErrInvalidKey
+ }
+ if q.ineqFiltHighSet && !q.ineqFiltHigh.Value().(*Key).Valid(false, aid, ns) {
+ return ErrInvalidKey
+ }
+ }
+ return nil
+}

Powered by Google App Engine
This is Rietveld 408576698