tdb/queryop.go

159 lines
3.8 KiB
Go

package tdb
import (
"bytes"
"errors"
"fmt"
// "sort"
"git.keganmyers.com/terribleplan/tdb/stringy"
bolt "go.etcd.io/bbolt"
)
type queryOpCreator func(*table, string, interface{}) (queryOpish, error)
var queryOps map[string]queryOpCreator = map[string]queryOpCreator{
"=": createEqualQueryOp,
// "in": createInQueryOp,
}
func createQueryOp(table *table, field, opType string, value interface{}) (queryOpish, error) {
create, ok := queryOps[opType]
if !ok {
return nil, fmt.Errorf("Unknown query operation '%s'", opType)
}
op, err := create(table, field, value)
if err != nil {
table.debugLogf("Failed creating query for '%s' on table '%s'", opType, table.name)
return nil, err
}
err = op.valid()
if err != nil {
table.debugLogf("Unable to form valid query for '%s' on table '%s'", opType, table.name)
return nil, err
}
return op, nil
}
type queryOpish interface {
rawIterable
Iterable
match(v dbPtrValue) bool
indexed() bool
valid() error
String() string
}
type queryOp struct {
table *table
field dbField
value []byte
index indexish
}
func createCommonQueryOp(table *table, field string, value interface{}) (*queryOp, error) {
index, ok := table.indicies[field]
if ok {
table.debugLogf("[query] found index for field '%s'", field)
} else {
table.debugLogf("[query] did not find index for field '%s'", field)
if len(table.indicies) == 0 {
table.debugLog("[query] (0 indicies found)")
} else {
for name, _ := range table.indicies {
table.debugLogf("[query] '%s' is indexed", name)
}
}
}
q := &queryOp{
table: table,
field: table.t.NamedField(field),
value: []byte(stringy.ToStringOrPanic(value)),
index: index,
}
return q, nil
}
// re-used for queryOpEqual
func (op *queryOp) indexed() bool {
return op.index != nil
}
type queryOpEqual queryOp
func createEqualQueryOp(table *table, field string, value interface{}) (queryOpish, error) {
qo, err := createCommonQueryOp(table, field, value)
if err != nil {
return nil, err
}
qoe := queryOpEqual(*qo)
return &qoe, nil
}
func (op *queryOpEqual) String() string {
return fmt.Sprintf("%s = %s", op.field.Name, op.value)
}
func (op *queryOpEqual) indexed() bool {
indexed := op.index != nil
op.table.debugLogf("[query] Table '%s'.'%s' indexed() -> %t", op.table.name, op.field.Name, indexed)
return indexed
}
func (op *queryOpEqual) valid() error {
return nil
}
func (op *queryOpEqual) match(pv dbPtrValue) bool {
return bytes.Equal([]byte(stringy.ValToStringOrPanic(pv.dangerous_Field(op.field))), op.value)
}
func (op *queryOpEqual) rawIterateKeys(i keyIteratorWithBucket, txs ...*Tx) error {
if op.index == nil {
return errors.New("This method is only applicable if the op is indexed")
}
return op.table.db.readTxHelper(func(tx *Tx) error {
db := op.table.bucket(tx)
return op.index.iteratePrefixed(tx, op.value, func(key []byte) (IterationSignal, error) {
return i(key, db)
})
}, txs...)
}
func (op *queryOpEqual) IterateKeys(i KeyIterator, txs ...*Tx) error {
return op.rawIterateKeys(func(k []byte, _ *bolt.Bucket) (IterationSignal, error) {
return i(k)
}, txs...)
}
func (op *queryOpEqual) Iterate(i Iterator, txs ...*Tx) error {
return op.rawIterateKeys(func(k []byte, b *bolt.Bucket) (IterationSignal, error) {
v, err := op.table.getWithinTx(b, k)
if err != nil {
op.table.debugLogf("[query] Encountered error while iterating (%s)", err)
return StopIteration, err
}
return i(v)
}, txs...)
}
func (op *queryOpEqual) iterateRaw(i rawIterator, txs ...*Tx) error {
return op.rawIterateKeys(func(k []byte, b *bolt.Bucket) (IterationSignal, error) {
v, err := op.table.getValWithinTx(b, k)
if err != nil {
op.table.debugLogf("[query] Encountered error while iterating (%s)", err)
return StopIteration, err
}
return i(v)
}, txs...)
}