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

Unified Diff: impl/memory/datastore_query.go

Issue 1285703002: Add testable interface for datastore. (Closed) Base URL: https://github.com/luci/gae.git@master
Patch Set: fixes after Raw change Created 5 years, 4 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
« no previous file with comments | « impl/memory/datastore_index_test.go ('k') | impl/memory/datastore_test.go » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: impl/memory/datastore_query.go
diff --git a/impl/memory/datastore_query.go b/impl/memory/datastore_query.go
index bf912f718b2aee98cb0edc88ad7226df5cfa698e..91e08750e81a978c7469ce588c443a8858d7aad0 100644
--- a/impl/memory/datastore_query.go
+++ b/impl/memory/datastore_query.go
@@ -5,7 +5,6 @@
package memory
import (
- "bytes"
"errors"
"fmt"
"math"
@@ -13,148 +12,8 @@ import (
ds "github.com/luci/gae/service/datastore"
"github.com/luci/gkvlite"
- "github.com/luci/luci-go/common/cmpbin"
)
-type qDirection bool
-
-const (
- qASC qDirection = true
- qDEC = false
-)
-
-var builtinQueryPrefix = []byte{0}
-var complexQueryPrefix = []byte{1}
-
-type qSortBy struct {
- prop string
- dir qDirection
-}
-
-func (q qSortBy) WriteBinary(buf *bytes.Buffer) {
- if q.dir == qASC {
- buf.WriteByte(0)
- } else {
- buf.WriteByte(1)
- }
- cmpbin.WriteString(buf, q.prop)
-}
-
-func (q *qSortBy) ReadBinary(buf *bytes.Buffer) error {
- dir, err := buf.ReadByte()
- if err != nil {
- return err
- }
- q.dir = dir == 0
- q.prop, _, err = cmpbin.ReadString(buf)
- return err
-}
-
-type qIndex struct {
- kind string
- ancestor bool
- sortby []qSortBy
-}
-
-func (i *qIndex) Builtin() bool {
- return !i.ancestor && len(i.sortby) <= 1
-}
-
-func (i *qIndex) Less(o *qIndex) bool {
- ibuf, obuf := &bytes.Buffer{}, &bytes.Buffer{}
- i.WriteBinary(ibuf)
- o.WriteBinary(obuf)
- return i.String() < o.String()
-}
-
-// Valid verifies that this qIndex doesn't have duplicate sortBy fields.
-func (i *qIndex) Valid() bool {
- names := map[string]bool{}
- for _, sb := range i.sortby {
- if names[sb.prop] {
- return false
- }
- names[sb.prop] = true
- }
- return true
-}
-
-func (i *qIndex) WriteBinary(buf *bytes.Buffer) {
- // TODO(riannucci): do a Grow call here?
- if i.Builtin() {
- buf.Write(builtinQueryPrefix)
- } else {
- buf.Write(complexQueryPrefix)
- }
- cmpbin.WriteString(buf, i.kind)
- if i.ancestor {
- buf.WriteByte(0)
- } else {
- buf.WriteByte(1)
- }
- cmpbin.WriteUint(buf, uint64(len(i.sortby)))
- for _, sb := range i.sortby {
- sb.WriteBinary(buf)
- }
-}
-
-func (i *qIndex) String() string {
- ret := &bytes.Buffer{}
- if i.Builtin() {
- ret.WriteRune('B')
- } else {
- ret.WriteRune('C')
- }
- ret.WriteRune(':')
- ret.WriteString(i.kind)
- if i.ancestor {
- ret.WriteString("|A")
- }
- for _, sb := range i.sortby {
- ret.WriteRune('/')
- if sb.dir == qDEC {
- ret.WriteRune('-')
- }
- ret.WriteString(sb.prop)
- }
- return ret.String()
-}
-
-func (i *qIndex) ReadBinary(buf *bytes.Buffer) error {
- // discard builtin/complex byte
- _, err := buf.ReadByte()
- if err != nil {
- return err
- }
-
- i.kind, _, err = cmpbin.ReadString(buf)
- if err != nil {
- return err
- }
- anc, err := buf.ReadByte()
- if err != nil {
- return err
- }
- i.ancestor = anc == 1
-
- numSorts, _, err := cmpbin.ReadUint(buf)
- if err != nil {
- return err
- }
- if numSorts > 64 {
- return fmt.Errorf("qIndex.ReadBinary: Got over 64 sort orders: %d", numSorts)
- }
- i.sortby = make([]qSortBy, numSorts)
- for idx := range i.sortby {
- err = (&i.sortby[idx]).ReadBinary(buf)
- if err != nil {
- return err
- }
- }
-
- return nil
-}
-
type queryOp int
const (
@@ -183,7 +42,7 @@ var queryOpMap = map[string]queryOp{
}
type queryFilter struct {
- field string
+ prop string
op queryOp
value interface{}
}
@@ -197,7 +56,7 @@ func parseFilter(f string, v interface{}) (ret queryFilter, err error) {
if op == qInvalid {
err = fmt.Errorf("datastore: invalid operator %q in filter %q", toks[1], f)
} else {
- ret.field = toks[0]
+ ret.prop = toks[0]
ret.op = op
ret.value = v
}
@@ -205,11 +64,6 @@ func parseFilter(f string, v interface{}) (ret queryFilter, err error) {
return
}
-type queryOrder struct {
- field string
- direction qDirection
-}
-
type queryCursor string
func (q queryCursor) String() string { return string(q) }
@@ -221,7 +75,7 @@ type queryImpl struct {
kind string
ancestor ds.Key
filter []queryFilter
- order []queryOrder
+ order []ds.IndexColumn
project []string
distinct bool
@@ -252,9 +106,9 @@ func (q *queryImpl) normalize() (ret *queryImpl) {
// if we supported the IN operator, we would check to see if there were
// multiple value operands here, but the go SDK doesn't support this.
if f.op.isEQOp() {
- eqProperties.Set([]byte(f.field), []byte{})
+ eqProperties.Set([]byte(f.prop), []byte{})
} else if f.op.isINEQOp() {
- ineqProperties.Set([]byte(f.field), []byte{})
+ ineqProperties.Set([]byte(f.prop), []byte{})
}
}
@@ -269,10 +123,10 @@ func (q *queryImpl) normalize() (ret *queryImpl) {
return true
})
- newOrders := []queryOrder{}
+ newOrders := []ds.IndexColumn{}
for _, o := range ret.order {
- if removeSet.Get([]byte(o.field)) == nil {
- removeSet.Set([]byte(o.field), []byte{})
+ if removeSet.Get([]byte(o.Property)) == nil {
+ removeSet.Set([]byte(o.Property), []byte{})
newOrders = append(newOrders, o)
}
}
@@ -285,8 +139,8 @@ func (q *queryImpl) normalize() (ret *queryImpl) {
// for f in ret.filters:
// if f.op != qExists:
// newFilters = append(newFilters, f)
- // if !removeSet.Has(f.field):
- // removeSet.InsertNoReplace(f.field)
+ // if !removeSet.Has(f.prop):
+ // removeSet.InsertNoReplace(f.prop)
// newFilters = append(newFilters, f)
//
// so ret.filters == newFilters becuase none of ret.filters has op == qExists
@@ -302,12 +156,12 @@ func (q *queryImpl) normalize() (ret *queryImpl) {
// However, since we don't support projection queries, this is moot.
if eqProperties.Get([]byte("__key__")) != nil {
- ret.order = []queryOrder{}
+ ret.order = []ds.IndexColumn{}
}
- newOrders = []queryOrder{}
+ newOrders = []ds.IndexColumn{}
for _, o := range ret.order {
- if o.field == "__key__" {
+ if o.Property == "__key__" {
newOrders = append(newOrders, o)
break
}
@@ -365,7 +219,7 @@ func (q *queryImpl) checkCorrectness(ns string, isTxn bool) (ret *queryImpl) {
ineqPropName := ""
for _, f := range ret.filter {
- if f.field == "__key__" {
+ if f.prop == "__key__" {
k, ok := f.value.(ds.Key)
if !ok {
ret.err = errors.New(
@@ -386,16 +240,16 @@ func (q *queryImpl) checkCorrectness(ns string, isTxn bool) (ret *queryImpl) {
// __key__ filter app is X but query app is X
// __key__ filter namespace is X but query namespace is X
}
- // if f.op == qEqual and f.field in ret.project_fields
+ // if f.op == qEqual and f.prop in ret.project_fields
// "cannot use projection on a proprety with an equality filter"
if f.op.isINEQOp() {
if ineqPropName == "" {
- ineqPropName = f.field
- } else if f.field != ineqPropName {
+ ineqPropName = f.prop
+ } else if f.prop != ineqPropName {
ret.err = fmt.Errorf(
"gae/memory: Only one inequality filter per query is supported. "+
- "Encountered both %s and %s", ineqPropName, f.field)
+ "Encountered both %s and %s", ineqPropName, f.prop)
return
}
}
@@ -406,26 +260,26 @@ func (q *queryImpl) checkCorrectness(ns string, isTxn bool) (ret *queryImpl) {
// "when group by properties are set."
if ineqPropName != "" && len(ret.order) != 0 {
- if ret.order[0].field != ineqPropName {
+ if ret.order[0].Property != ineqPropName {
ret.err = fmt.Errorf(
"gae/memory: The first sort property must be the same as the property "+
"to which the inequality filter is applied. In your query "+
"the first sort property is %s but the inequality filter "+
- "is on %s", ret.order[0].field, ineqPropName)
+ "is on %s", ret.order[0].Property, ineqPropName)
return
}
}
if ret.kind == "" {
for _, f := range ret.filter {
- if f.field != "__key__" {
+ if f.prop != "__key__" {
ret.err = errors.New(
"gae/memory: kind is required for non-__key__ filters")
return
}
}
for _, o := range ret.order {
- if o.field != "__key__" || o.direction != qASC {
+ if o.Property != "__key__" || o.Direction != ds.ASCENDING {
ret.err = errors.New(
"gae/memory: kind is required for all orders except __key__ ascending")
return
@@ -435,7 +289,7 @@ func (q *queryImpl) checkCorrectness(ns string, isTxn bool) (ret *queryImpl) {
return
}
-func (q *queryImpl) calculateIndex() *qIndex {
+func (q *queryImpl) calculateIndex() *ds.IndexDefinition {
// as a nod to simplicity in this code, we'll require that a single index
// is able to service the entire query. E.g. no zigzag merge joins or
// multiqueries. This will mean that the user will need to rely on
@@ -453,7 +307,7 @@ func (q *queryImpl) calculateIndex() *qIndex {
func (q *queryImpl) clone() *queryImpl {
ret := *q
ret.filter = append([]queryFilter(nil), q.filter...)
- ret.order = append([]queryOrder(nil), q.order...)
+ ret.order = append([]ds.IndexColumn(nil), q.order...)
ret.project = append([]string(nil), q.project...)
return &ret
}
@@ -494,18 +348,18 @@ func (q *queryImpl) Filter(fStr string, val interface{}) ds.Query {
return q
}
-func (q *queryImpl) Order(field string) ds.Query {
+func (q *queryImpl) Order(prop string) ds.Query {
q = q.clone()
- field = strings.TrimSpace(field)
- o := queryOrder{field, qASC}
- if strings.HasPrefix(field, "-") {
- o.direction = qDEC
- o.field = strings.TrimSpace(field[1:])
- } else if strings.HasPrefix(field, "+") {
- q.err = fmt.Errorf("datastore: invalid order: %q", field)
+ prop = strings.TrimSpace(prop)
+ o := ds.IndexColumn{Property: prop}
+ if strings.HasPrefix(prop, "-") {
+ o.Direction = ds.DESCENDING
+ o.Property = strings.TrimSpace(prop[1:])
+ } else if strings.HasPrefix(prop, "+") {
+ q.err = fmt.Errorf("datastore: invalid order: %q", prop)
return q
}
- if len(o.field) == 0 {
+ if len(o.Property) == 0 {
q.err = errors.New("datastore: empty order")
return q
}
« no previous file with comments | « impl/memory/datastore_index_test.go ('k') | impl/memory/datastore_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698