add tree iterator

This commit is contained in:
Asdine El Hrychy
2024-10-05 16:45:14 +04:00
parent f1f933e49b
commit c4f9270305
12 changed files with 204 additions and 111 deletions

View File

@@ -43,10 +43,8 @@ func ExecSQL(ctx context.Context, db *chai.DB, r io.Reader, w io.Writer) error {
}
err = res.Iterate(func(r database.Row) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
if err := ctx.Err(); err != nil {
return err
}
return enc.Encode(r)

View File

@@ -166,7 +166,6 @@ func (sh *Shell) runExecutor(ctx context.Context, promptExecCh chan queryTask) e
continue
}
// if showtime is true, ensure it's a query, and it was executed.
if displayTime {
fmt.Fprintf(input.w, "Time: %s\n", time.Since(start))
}

View File

@@ -279,11 +279,9 @@ func (rs *Rows) iterate(ctx context.Context) {
if errors.Is(err, errStop) || err == nil {
return
}
if err != nil {
rs.c <- Row{
err: err,
}
return
rs.c <- Row{
err: err,
}
}

View File

@@ -169,6 +169,7 @@ github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20190129172621-c8b1d7a94ddf
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM=
github.com/aclements/go-gg v0.0.0-20170118225347-6dbb4e4fefb0/go.mod h1:55qNq4vcpkIuHowELi5C8e+1yUHtoLoOUR9QU5j7Tes=
@@ -188,6 +189,7 @@ github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -205,16 +207,7 @@ github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/J
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo=
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
github.com/charmbracelet/x/ansi v0.9.3 h1:BXt5DHS/MKF+LjuK4huWrC6NCvHtexww7dMayh6GXd0=
github.com/charmbracelet/x/ansi v0.9.3/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
@@ -239,6 +232,7 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE=
github.com/dromara/carbon/v2 v2.3.12/go.mod h1:HNsedGzXGuNciZImYP2OMnpiwq/vhIstR/vn45ib5cI=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@@ -722,8 +716,6 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -791,8 +783,6 @@ golang.org/x/telemetry v0.0.0-20250807160809-1a19826ec488/go.mod h1:fGb/2+tgXXjh
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=

View File

@@ -79,20 +79,24 @@ func (idx *Index) Exists(vs []types.Value) (bool, *tree.Key, error) {
var found bool
var dKey *tree.Key
err := idx.Tree.IterateOnRange(&tree.Range{Min: seek, Max: seek}, false, func(k *tree.Key, _ []byte) error {
values, err := k.Decode()
it, err := idx.Tree.Iterator(&tree.Range{Min: seek, Max: seek})
if err != nil {
return false, nil, err
}
defer it.Close()
for it.First(); it.Valid(); it.Next() {
k, err := it.Key().Decode()
if err != nil {
return err
return false, nil, err
}
dKey = tree.NewEncodedKey(types.AsByteSlice(values[len(values)-1]))
dKey = tree.NewEncodedKey(types.AsByteSlice(k[len(k)-1]))
found = true
return errStop
})
if err == errStop {
err = nil
break
}
return found, dKey, err
return found, dKey, it.Error()
}
// Delete all the references to the key from the index.
@@ -132,11 +136,20 @@ func (idx *Index) IterateOnRange(rng *tree.Range, reverse bool, fn func(key *tre
}
func (idx *Index) iterateOnRange(rng *tree.Range, reverse bool, fn func(itmKey *tree.Key, key *tree.Key) error) error {
return idx.Tree.IterateOnRange(rng, reverse, idx.iterator(fn))
}
it, err := idx.Tree.Iterator(rng)
if err != nil {
return err
}
defer it.Close()
func (idx *Index) iterator(fn func(itmKey *tree.Key, key *tree.Key) error) func(k *tree.Key, d []byte) error {
return func(k *tree.Key, _ []byte) error {
if !reverse {
it.First()
} else {
it.Last()
}
for it.Valid() {
k := it.Key()
// we don't care about the value, we just want to extract the key
// which is the last element of the encoded array
values, err := k.Decode()
@@ -146,8 +159,19 @@ func (idx *Index) iterator(fn func(itmKey *tree.Key, key *tree.Key) error) func(
pk := tree.NewEncodedKey(types.AsByteSlice(values[len(values)-1]))
return fn(k, pk)
err = fn(k, pk)
if err != nil {
return err
}
if !reverse {
it.Next()
} else {
it.Prev()
}
}
return it.Error()
}
// Truncate deletes all the index data.

View File

@@ -7,7 +7,7 @@ import (
)
type Row interface {
// Iterate goes through all the fields of the row and calls the given function
// Iterate goes through all the columns of the row and calls the given function
// by passing the column name
Iterate(fn func(column string, value types.Value) error) error

View File

@@ -36,7 +36,7 @@ func (t *Table) Insert(r row.Row) (*tree.Key, Row, error) {
return nil, nil, errors.New("cannot write to read-only table")
}
key, isRowid, err := t.generateKey(t.Info, r)
key, isRowid, err := t.generateKey(r)
if err != nil {
return nil, nil, err
}
@@ -167,11 +167,39 @@ func (t *Table) IterateOnRange(rng *Range, reverse bool, fn func(key *tree.Key,
Row: &e,
}
return t.Tree.IterateOnRange(r, reverse, func(k *tree.Key, enc []byte) error {
it, err := t.Tree.Iterator(r)
if err != nil {
return err
}
defer it.Close()
if reverse {
it.Last()
} else {
it.First()
}
for it.Valid() {
k := it.Key()
enc, err := it.Value()
if err != nil {
return err
}
row.key = k
e.encoded = enc
return fn(k, &row)
})
if err := fn(k, &row); err != nil {
return err
}
if reverse {
it.Prev()
} else {
it.Next()
}
}
return it.Error()
}
// GetRow returns one row by key.
@@ -198,7 +226,7 @@ func (t *Table) GetRow(key *tree.Key) (Row, error) {
// if there are no primary key in the table, a default
// key is generated, called the rowid.
// It returns a boolean indicating whether the key is a rowid or not.
func (t *Table) generateKey(info *TableInfo, r row.Row) (*tree.Key, bool, error) {
func (t *Table) generateKey(r row.Row) (*tree.Key, bool, error) {
if pk := t.Info.PrimaryKey; pk != nil {
vs := make([]types.Value, 0, len(pk.Columns))
for _, c := range pk.Columns {

View File

@@ -4,7 +4,6 @@ import (
"testing"
"github.com/chaisql/chai/internal/testutil"
"github.com/chaisql/chai/internal/tree"
"github.com/stretchr/testify/require"
)
@@ -71,11 +70,14 @@ func TestReIndex(t *testing.T) {
}
i := 0
err = idx.Tree.IterateOnRange(nil, false, func(*tree.Key, []byte) error {
i++
return nil
})
it, err := idx.Tree.Iterator(nil)
require.NoError(t, err)
defer it.Close()
for it.First(); it.Valid(); it.Next() {
i++
}
require.NoError(t, it.Error())
if shouldBeIndexed {
require.Equal(t, 2, i)
} else {

View File

@@ -113,7 +113,26 @@ func (op *TempTreeSortOperator) Iterate(in *environment.Environment, fn func(out
var newEnv environment.Environment
newEnv.SetOuter(in)
var br database.BasicRow
return tr.IterateOnRange(nil, op.Desc, func(k *tree.Key, data []byte) error {
it, err := tr.Iterator(nil)
if err != nil {
return err
}
defer it.Close()
if op.Desc {
it.Last()
} else {
it.First()
}
for it.Valid() {
k := it.Key()
data, err := it.Value()
if err != nil {
return err
}
kv, err := k.Decode()
if err != nil {
return err
@@ -137,8 +156,19 @@ func (op *TempTreeSortOperator) Iterate(in *environment.Environment, fn func(out
newEnv.SetRow(&br)
return fn(&newEnv)
})
err = fn(&newEnv)
if err != nil {
return err
}
if op.Desc {
it.Prev()
} else {
it.Next()
}
}
return it.Error()
}
func (op *TempTreeSortOperator) String() string {

View File

@@ -43,7 +43,7 @@ func (it *UnionOperator) Columns(env *environment.Environment) ([]string, error)
}
// Iterate iterates over all the streams and returns their union.
func (it *UnionOperator) Iterate(in *environment.Environment, fn func(out *environment.Environment) error) (err error) {
func (op *UnionOperator) Iterate(in *environment.Environment, fn func(out *environment.Environment) error) (err error) {
var temp *tree.Tree
var cleanup func() error
@@ -60,7 +60,7 @@ func (it *UnionOperator) Iterate(in *environment.Environment, fn func(out *envir
// to deduplicate them
var buf []byte
for _, s := range it.Streams {
for _, s := range op.Streams {
err := s.Iterate(in, func(out *environment.Environment) error {
buf = buf[:0]
@@ -123,8 +123,21 @@ func (it *UnionOperator) Iterate(in *environment.Environment, fn func(out *envir
newEnv.SetOuter(in)
var basicRow database.BasicRow
// iterate over the temporary index
return temp.IterateOnRange(nil, false, func(key *tree.Key, value []byte) error {
it, err := temp.Iterator(nil)
if err != nil {
return err
}
defer it.Close()
for it.First(); it.Valid(); it.Next() {
key := it.Key()
value, err := it.Value()
if err != nil {
return err
}
kv, err := key.Decode()
if err != nil {
return err
@@ -144,8 +157,13 @@ func (it *UnionOperator) Iterate(in *environment.Environment, fn func(out *envir
basicRow.ResetWith(tableName, pk, obj)
newEnv.SetRow(&basicRow)
return fn(&newEnv)
})
err = fn(&newEnv)
if err != nil {
return err
}
}
return it.Error()
}
func (it *UnionOperator) String() string {

View File

@@ -76,12 +76,16 @@ func NewTransient(session engine.Session, ns Namespace, order SortOrder) (*Tree,
}
// ensure the namespace is not in use
err := t.IterateOnRange(nil, false, func(k *Key, b []byte) error {
return errors.Errorf("namespace %d is already in use", ns)
})
it, err := t.Iterator(nil)
if err != nil {
return nil, nil, err
}
defer it.Close()
it.First()
if it.Valid() {
return nil, nil, errors.Errorf("namespace %d is already in use", ns)
}
return &t, t.Truncate, nil
}
@@ -163,8 +167,19 @@ func (t *Tree) Truncate() error {
return t.Session.DeleteRange(encoding.EncodeInt(nil, int64(t.Namespace)), encoding.EncodeInt(nil, int64(t.Namespace)+1))
}
// IterateOnRange iterates on all keys that are in the given range.
func (t *Tree) IterateOnRange(rng *Range, reverse bool, fn func(*Key, []byte) error) error {
type Iterator struct {
engine.Iterator
k Key
}
func (it *Iterator) Key() *Key {
it.k.Encoded = it.Iterator.Key()
it.k.values = nil
return &it.k
}
func (t *Tree) Iterator(rng *Range) (*Iterator, error) {
var start, end []byte
var err error
@@ -186,7 +201,7 @@ func (t *Tree) IterateOnRange(rng *Range, reverse bool, fn func(*Key, []byte) er
start, end, err = t.buildExclusiveBoundaries(min, max, desc)
}
if err != nil {
return err
return nil, err
}
opts := engine.IterOptions{
@@ -195,42 +210,12 @@ func (t *Tree) IterateOnRange(rng *Range, reverse bool, fn func(*Key, []byte) er
}
it, err := t.Session.Iterator(&opts)
if err != nil {
return err
}
defer it.Close()
if !reverse {
it.First()
} else {
it.Last()
return nil, err
}
var k Key
for it.Valid() {
k.Encoded = it.Key()
k.values = nil
v, err := it.Value()
if err != nil {
return err
}
if len(v) == 0 || v[0] == 0 {
v = nil
}
err = fn(&k, v)
if err != nil {
return err
}
if !reverse {
it.Next()
} else {
it.Prev()
}
}
return it.Error()
return &Iterator{
Iterator: it,
}, nil
}
func (t *Tree) isDescRange(rng *Range) bool {
@@ -248,7 +233,7 @@ func (t *Tree) buildInclusiveBoundaries(min, max *Key, desc bool) (start []byte,
if min == nil {
start, err = t.buildMinKeyForType(max, desc)
} else {
start, err = t.buildStartKeyInclusive(min, desc)
start, err = t.buildStartKeyInclusive(min)
}
if err != nil {
return
@@ -256,7 +241,7 @@ func (t *Tree) buildInclusiveBoundaries(min, max *Key, desc bool) (start []byte,
if max == nil {
end, err = t.buildMaxKeyForType(min, desc)
} else {
end, err = t.buildEndKeyInclusive(max, desc)
end, err = t.buildEndKeyInclusive(max)
}
return
}
@@ -265,7 +250,7 @@ func (t *Tree) buildExclusiveBoundaries(min, max *Key, desc bool) (start []byte,
if min == nil {
start, err = t.buildMinKeyForType(max, desc)
} else {
start, err = t.buildStartKeyExclusive(min, desc)
start, err = t.buildStartKeyExclusive(min)
}
if err != nil {
return
@@ -273,7 +258,7 @@ func (t *Tree) buildExclusiveBoundaries(min, max *Key, desc bool) (start []byte,
if max == nil {
end, err = t.buildMaxKeyForType(min, desc)
} else {
end, err = t.buildEndKeyExclusive(max, desc)
end, err = t.buildEndKeyExclusive(max)
}
return
}
@@ -343,11 +328,11 @@ func (t *Tree) buildLastKey() []byte {
return append(buf, 0xFF)
}
func (t *Tree) buildStartKeyInclusive(key *Key, desc bool) ([]byte, error) {
func (t *Tree) buildStartKeyInclusive(key *Key) ([]byte, error) {
return key.Encode(t.Namespace, t.Order)
}
func (t *Tree) buildStartKeyExclusive(key *Key, desc bool) ([]byte, error) {
func (t *Tree) buildStartKeyExclusive(key *Key) ([]byte, error) {
b, err := key.Encode(t.Namespace, t.Order)
if err != nil {
return nil, err
@@ -356,7 +341,7 @@ func (t *Tree) buildStartKeyExclusive(key *Key, desc bool) ([]byte, error) {
return append(b, 0xFF), nil
}
func (t *Tree) buildEndKeyInclusive(key *Key, desc bool) ([]byte, error) {
func (t *Tree) buildEndKeyInclusive(key *Key) ([]byte, error) {
b, err := key.Encode(t.Namespace, t.Order)
if err != nil {
return nil, err
@@ -365,7 +350,7 @@ func (t *Tree) buildEndKeyInclusive(key *Key, desc bool) ([]byte, error) {
return append(b, 0xFF), nil
}
func (t *Tree) buildEndKeyExclusive(key *Key, desc bool) ([]byte, error) {
func (t *Tree) buildEndKeyExclusive(key *Key) ([]byte, error) {
return key.Encode(t.Namespace, t.Order)
}

View File

@@ -106,10 +106,14 @@ func TestTreeTruncate(t *testing.T) {
err = tr.Truncate()
require.NoError(t, err)
err = tr.IterateOnRange(nil, false, func(k *tree.Key, b []byte) error {
return fmt.Errorf("expected no keys")
})
it, err := tr.Iterator(nil)
require.NoError(t, err)
defer it.Close()
it.First()
if it.Valid() {
t.Errorf("expected no keys")
}
})
}
@@ -321,11 +325,28 @@ func TestTreeIterateOnRange(t *testing.T) {
var results []string
err := tt.IterateOnRange(&rng, reversed, func(k *tree.Key, _ []byte) error {
results = append(results, k.String())
return nil
})
it, err := tt.Iterator(&rng)
require.NoError(t, err)
defer it.Close()
if reversed {
it.Last()
} else {
it.First()
}
for it.Valid() {
k := it.Key()
results = append(results, k.String())
if reversed {
it.Prev()
} else {
it.Next()
}
}
require.NoError(t, it.Error())
var want []string
if !reversed {