159 lines
3.8 KiB
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...)
|
||
|
}
|