Dependency: Update github.com/hashicorp/go-memdb to v1.0.3 (#6626)

This commit is contained in:
Matt Keeler 2019-10-16 12:10:12 -04:00 committed by GitHub
parent 979ad7fecb
commit e8ee7c42a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 477 additions and 29 deletions

2
go.mod
View File

@ -31,7 +31,7 @@ require (
github.com/hashicorp/go-cleanhttp v0.5.1 github.com/hashicorp/go-cleanhttp v0.5.1
github.com/hashicorp/go-discover v0.0.0-20190403160810-22221edb15cd github.com/hashicorp/go-discover v0.0.0-20190403160810-22221edb15cd
github.com/hashicorp/go-hclog v0.9.2 github.com/hashicorp/go-hclog v0.9.2
github.com/hashicorp/go-memdb v0.0.0-20180223233045-1289e7fffe71 github.com/hashicorp/go-memdb v1.0.3
github.com/hashicorp/go-msgpack v0.5.5 github.com/hashicorp/go-msgpack v0.5.5
github.com/hashicorp/go-multierror v1.0.0 github.com/hashicorp/go-multierror v1.0.0
github.com/hashicorp/go-plugin v1.0.1 github.com/hashicorp/go-plugin v1.0.1

6
go.sum
View File

@ -127,8 +127,10 @@ github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxC
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-memdb v0.0.0-20180223233045-1289e7fffe71 h1:yxxFgVz31vFoKKTtRUNbXLNe4GFnbLKqg+0N7yG42L8= github.com/hashicorp/go-immutable-radix v1.1.0 h1:vN9wG1D6KG6YHRTWr8512cxGOVgTMEfgEdSj/hr8MPc=
github.com/hashicorp/go-memdb v0.0.0-20180223233045-1289e7fffe71/go.mod h1:kbfItVoBJwCfKXDXN4YoAXjxcFVZ7MRrJzyTX6H4giE= github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-memdb v1.0.3 h1:iiqzNk8jKB6/sLRj623Ui/Vi1zf21LOUpgzGjTge6a8=
github.com/hashicorp/go-memdb v1.0.3/go.mod h1:LWQ8R70vPrS4OEY9k28D2z8/Zzyu34NVzeRibGAzHO0=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=

View File

@ -0,0 +1,9 @@
# 1.1.0 (May 22nd, 2019)
FEATURES
* Add `SeekLowerBound` to allow for range scans. [[GH-24](https://github.com/hashicorp/go-immutable-radix/pull/24)]
# 1.0.0 (August 30th, 2018)
* go mod adopted

View File

@ -39,3 +39,28 @@ if string(m) != "foo" {
} }
``` ```
Here is an example of performing a range scan of the keys.
```go
// Create a tree
r := iradix.New()
r, _, _ = r.Insert([]byte("001"), 1)
r, _, _ = r.Insert([]byte("002"), 2)
r, _, _ = r.Insert([]byte("005"), 5)
r, _, _ = r.Insert([]byte("010"), 10)
r, _, _ = r.Insert([]byte("100"), 10)
// Range scan over the keys that sort lexicographically between [003, 050)
it := r.Root().Iterator()
it.SeekLowerBound([]byte("003"))
for key, _, ok := it.Next(); ok; key, _, ok = it.Next() {
if key >= "050" {
break
}
fmt.Println(key)
}
// Output:
// 005
// 010
```

View File

@ -1,6 +1,8 @@
package iradix package iradix
import "bytes" import (
"bytes"
)
// Iterator is used to iterate over a set of nodes // Iterator is used to iterate over a set of nodes
// in pre-order // in pre-order
@ -53,6 +55,101 @@ func (i *Iterator) SeekPrefix(prefix []byte) {
i.SeekPrefixWatch(prefix) i.SeekPrefixWatch(prefix)
} }
func (i *Iterator) recurseMin(n *Node) *Node {
// Traverse to the minimum child
if n.leaf != nil {
return n
}
if len(n.edges) > 0 {
// Add all the other edges to the stack (the min node will be added as
// we recurse)
i.stack = append(i.stack, n.edges[1:])
return i.recurseMin(n.edges[0].node)
}
// Shouldn't be possible
return nil
}
// SeekLowerBound is used to seek the iterator to the smallest key that is
// greater or equal to the given key. There is no watch variant as it's hard to
// predict based on the radix structure which node(s) changes might affect the
// result.
func (i *Iterator) SeekLowerBound(key []byte) {
// Wipe the stack. Unlike Prefix iteration, we need to build the stack as we
// go because we need only a subset of edges of many nodes in the path to the
// leaf with the lower bound.
i.stack = []edges{}
n := i.node
search := key
found := func(n *Node) {
i.node = n
i.stack = append(i.stack, edges{edge{node: n}})
}
for {
// Compare current prefix with the search key's same-length prefix.
var prefixCmp int
if len(n.prefix) < len(search) {
prefixCmp = bytes.Compare(n.prefix, search[0:len(n.prefix)])
} else {
prefixCmp = bytes.Compare(n.prefix, search)
}
if prefixCmp > 0 {
// Prefix is larger, that means the lower bound is greater than the search
// and from now on we need to follow the minimum path to the smallest
// leaf under this subtree.
n = i.recurseMin(n)
if n != nil {
found(n)
}
return
}
if prefixCmp < 0 {
// Prefix is smaller than search prefix, that means there is no lower
// bound
i.node = nil
return
}
// Prefix is equal, we are still heading for an exact match. If this is a
// leaf we're done.
if n.leaf != nil {
if bytes.Compare(n.leaf.key, key) < 0 {
i.node = nil
return
}
found(n)
return
}
// Consume the search prefix
if len(n.prefix) > len(search) {
search = []byte{}
} else {
search = search[len(n.prefix):]
}
// Otherwise, take the lower bound next edge.
idx, lbNode := n.getLowerBoundEdge(search[0])
if lbNode == nil {
i.node = nil
return
}
// Create stack edges for the all strictly higher edges in this node.
if idx+1 < len(n.edges) {
i.stack = append(i.stack, n.edges[idx+1:])
}
i.node = lbNode
// Recurse
n = lbNode
}
}
// Next returns the next node in order // Next returns the next node in order
func (i *Iterator) Next() ([]byte, interface{}, bool) { func (i *Iterator) Next() ([]byte, interface{}, bool) {
// Initialize our stack if needed // Initialize our stack if needed

View File

@ -79,6 +79,18 @@ func (n *Node) getEdge(label byte) (int, *Node) {
return -1, nil return -1, nil
} }
func (n *Node) getLowerBoundEdge(label byte) (int, *Node) {
num := len(n.edges)
idx := sort.Search(num, func(i int) bool {
return n.edges[i].label >= label
})
// we want lower bound behavior so return even if it's not an exact match
if idx < num {
return idx, n.edges[idx].node
}
return -1, nil
}
func (n *Node) delEdge(label byte) { func (n *Node) delEdge(label byte) {
num := len(n.edges) num := len(n.edges)
idx := sort.Search(num, func(i int) bool { idx := sort.Search(num, func(i int) bool {

View File

@ -22,3 +22,5 @@ _testmain.go
*.exe *.exe
*.test *.test
*.prof *.prof
.idea

View File

@ -1,7 +0,0 @@
language: go
go:
- "1.10"
script:
- go test

View File

@ -1,4 +1,4 @@
# go-memdb # go-memdb [![CircleCI](https://circleci.com/gh/hashicorp/go-memdb/tree/master.svg?style=svg)](https://circleci.com/gh/hashicorp/go-memdb/tree/master)
Provides the `memdb` package that implements a simple in-memory database Provides the `memdb` package that implements a simple in-memory database
built on immutable radix trees. The database provides Atomicity, Consistency built on immutable radix trees. The database provides Atomicity, Consistency
@ -21,7 +21,7 @@ The database provides the following:
a single field index, or more advanced compound field indexes. Certain types like a single field index, or more advanced compound field indexes. Certain types like
UUID can be efficiently compressed from strings into byte indexes for reduced UUID can be efficiently compressed from strings into byte indexes for reduced
storage requirements. storage requirements.
* Watches - Callers can populate a watch set as part of a query, which can be used to * Watches - Callers can populate a watch set as part of a query, which can be used to
detect when a modification has been made to the database which affects the query detect when a modification has been made to the database which affects the query
results. This lets callers easily watch for changes in the database in a very general results. This lets callers easily watch for changes in the database in a very general

5
vendor/github.com/hashicorp/go-memdb/go.mod generated vendored Normal file
View File

@ -0,0 +1,5 @@
module github.com/hashicorp/go-memdb
go 1.12
require github.com/hashicorp/go-immutable-radix v1.1.0

6
vendor/github.com/hashicorp/go-memdb/go.sum generated vendored Normal file
View File

@ -0,0 +1,6 @@
github.com/hashicorp/go-immutable-radix v1.1.0 h1:vN9wG1D6KG6YHRTWr8512cxGOVgTMEfgEdSj/hr8MPc=
github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=

View File

@ -3,6 +3,7 @@ package memdb
import ( import (
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"errors"
"fmt" "fmt"
"reflect" "reflect"
"strings" "strings"
@ -63,9 +64,16 @@ func (s *StringFieldIndex) FromObject(obj interface{}) (bool, []byte, error) {
v = reflect.Indirect(v) // Dereference the pointer if any v = reflect.Indirect(v) // Dereference the pointer if any
fv := v.FieldByName(s.Field) fv := v.FieldByName(s.Field)
if !fv.IsValid() { isPtr := fv.Kind() == reflect.Ptr
fv = reflect.Indirect(fv)
if !isPtr && !fv.IsValid() {
return false, nil, return false, nil,
fmt.Errorf("field '%s' for %#v is invalid", s.Field, obj) fmt.Errorf("field '%s' for %#v is invalid %v ", s.Field, obj, isPtr)
}
if isPtr && !fv.IsValid() {
val := ""
return true, []byte(val), nil
} }
val := fv.String() val := fv.String()
@ -188,6 +196,16 @@ func (s *StringSliceFieldIndex) PrefixFromArgs(args ...interface{}) ([]byte, err
// StringMapFieldIndex is used to extract a field of type map[string]string // StringMapFieldIndex is used to extract a field of type map[string]string
// from an object using reflection and builds an index on that field. // from an object using reflection and builds an index on that field.
//
// Note that although FromArgs in theory supports using either one or
// two arguments, there is a bug: FromObject only creates an index
// using key/value, and does not also create an index using key. This
// means a lookup using one argument will never actually work.
//
// It is currently left as-is to prevent backwards compatibility
// issues.
//
// TODO: Fix this in the next major bump.
type StringMapFieldIndex struct { type StringMapFieldIndex struct {
Field string Field string
Lowercase bool Lowercase bool
@ -233,6 +251,8 @@ func (s *StringMapFieldIndex) FromObject(obj interface{}) (bool, [][]byte, error
return true, vals, nil return true, vals, nil
} }
// WARNING: Because of a bug in FromObject, this function will never return
// a value when using the single-argument version.
func (s *StringMapFieldIndex) FromArgs(args ...interface{}) ([]byte, error) { func (s *StringMapFieldIndex) FromArgs(args ...interface{}) ([]byte, error) {
if len(args) > 2 || len(args) == 0 { if len(args) > 2 || len(args) == 0 {
return nil, fmt.Errorf("must provide one or two arguments") return nil, fmt.Errorf("must provide one or two arguments")
@ -262,6 +282,79 @@ func (s *StringMapFieldIndex) FromArgs(args ...interface{}) ([]byte, error) {
return []byte(key), nil return []byte(key), nil
} }
// IntFieldIndex is used to extract an int field from an object using
// reflection and builds an index on that field.
type IntFieldIndex struct {
Field string
}
func (i *IntFieldIndex) FromObject(obj interface{}) (bool, []byte, error) {
v := reflect.ValueOf(obj)
v = reflect.Indirect(v) // Dereference the pointer if any
fv := v.FieldByName(i.Field)
if !fv.IsValid() {
return false, nil,
fmt.Errorf("field '%s' for %#v is invalid", i.Field, obj)
}
// Check the type
k := fv.Kind()
size, ok := IsIntType(k)
if !ok {
return false, nil, fmt.Errorf("field %q is of type %v; want an int", i.Field, k)
}
// Get the value and encode it
val := fv.Int()
buf := make([]byte, size)
binary.PutVarint(buf, val)
return true, buf, nil
}
func (i *IntFieldIndex) FromArgs(args ...interface{}) ([]byte, error) {
if len(args) != 1 {
return nil, fmt.Errorf("must provide only a single argument")
}
v := reflect.ValueOf(args[0])
if !v.IsValid() {
return nil, fmt.Errorf("%#v is invalid", args[0])
}
k := v.Kind()
size, ok := IsIntType(k)
if !ok {
return nil, fmt.Errorf("arg is of type %v; want a int", k)
}
val := v.Int()
buf := make([]byte, size)
binary.PutVarint(buf, val)
return buf, nil
}
// IsIntType returns whether the passed type is a type of int and the number
// of bytes needed to encode the type.
func IsIntType(k reflect.Kind) (size int, okay bool) {
switch k {
case reflect.Int:
return binary.MaxVarintLen64, true
case reflect.Int8:
return 2, true
case reflect.Int16:
return binary.MaxVarintLen16, true
case reflect.Int32:
return binary.MaxVarintLen32, true
case reflect.Int64:
return binary.MaxVarintLen64, true
default:
return 0, false
}
}
// UintFieldIndex is used to extract a uint field from an object using // UintFieldIndex is used to extract a uint field from an object using
// reflection and builds an index on that field. // reflection and builds an index on that field.
type UintFieldIndex struct { type UintFieldIndex struct {
@ -540,7 +633,7 @@ func (c *CompoundIndex) FromObject(raw interface{}) (bool, []byte, error) {
func (c *CompoundIndex) FromArgs(args ...interface{}) ([]byte, error) { func (c *CompoundIndex) FromArgs(args ...interface{}) ([]byte, error) {
if len(args) != len(c.Indexes) { if len(args) != len(c.Indexes) {
return nil, fmt.Errorf("less arguments than index fields") return nil, fmt.Errorf("non-equivalent argument count and index fields")
} }
var out []byte var out []byte
for i, arg := range args { for i, arg := range args {
@ -579,3 +672,177 @@ func (c *CompoundIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) {
} }
return out, nil return out, nil
} }
// CompoundMultiIndex is used to build an index using multiple
// sub-indexes.
//
// Unlike CompoundIndex, CompoundMultiIndex can have both
// SingleIndexer and MultiIndexer sub-indexers. However, each
// MultiIndexer adds considerable overhead/complexity in terms of
// the number of indexes created under-the-hood. It is not suggested
// to use more than one or two, if possible.
//
// Another change from CompoundIndexer is that if AllowMissing is
// set, not only is it valid to have empty index fields, but it will
// still create index values up to the first empty index. This means
// that if you have a value with an empty field, rather than using a
// prefix for lookup, you can simply pass in less arguments. As an
// example, if {Foo, Bar} is indexed but Bar is missing for a value
// and AllowMissing is set, an index will still be created for {Foo}
// and it is valid to do a lookup passing in only Foo as an argument.
// Note that the ordering isn't guaranteed -- it's last-insert wins,
// but this is true if you have two objects that have the same
// indexes not using AllowMissing anyways.
//
// Because StringMapFieldIndexers can take a varying number of args,
// it is currently a requirement that whenever it is used, two
// arguments must _always_ be provided for it. In theory we only
// need one, except a bug in that indexer means the single-argument
// version will never work. You can leave the second argument nil,
// but it will never produce a value. We support this for whenever
// that bug is fixed, likely in a next major version bump.
//
// Prefix-based indexing is not currently supported.
type CompoundMultiIndex struct {
Indexes []Indexer
// AllowMissing results in an index based on only the indexers
// that return data. If true, you may end up with 2/3 columns
// indexed which might be useful for an index scan. Otherwise,
// CompoundMultiIndex requires all indexers to be satisfied.
AllowMissing bool
}
func (c *CompoundMultiIndex) FromObject(raw interface{}) (bool, [][]byte, error) {
// At each entry, builder is storing the results from the next index
builder := make([][][]byte, 0, len(c.Indexes))
// Start with something higher to avoid resizing if possible
out := make([][]byte, 0, len(c.Indexes)^3)
forloop:
// This loop goes through each indexer and adds the value(s) provided to the next
// entry in the slice. We can then later walk it like a tree to construct the indices.
for i, idxRaw := range c.Indexes {
switch idx := idxRaw.(type) {
case SingleIndexer:
ok, val, err := idx.FromObject(raw)
if err != nil {
return false, nil, fmt.Errorf("single sub-index %d error: %v", i, err)
}
if !ok {
if c.AllowMissing {
break forloop
} else {
return false, nil, nil
}
}
builder = append(builder, [][]byte{val})
case MultiIndexer:
ok, vals, err := idx.FromObject(raw)
if err != nil {
return false, nil, fmt.Errorf("multi sub-index %d error: %v", i, err)
}
if !ok {
if c.AllowMissing {
break forloop
} else {
return false, nil, nil
}
}
// Add each of the new values to each of the old values
builder = append(builder, vals)
default:
return false, nil, fmt.Errorf("sub-index %d does not satisfy either SingleIndexer or MultiIndexer", i)
}
}
// We are walking through the builder slice essentially in a depth-first fashion,
// building the prefix and leaves as we go. If AllowMissing is false, we only insert
// these full paths to leaves. Otherwise, we also insert each prefix along the way.
// This allows for lookup in FromArgs when AllowMissing is true that does not contain
// the full set of arguments. e.g. for {Foo, Bar} where an object has only the Foo
// field specified as "abc", it is valid to call FromArgs with just "abc".
var walkVals func([]byte, int)
walkVals = func(currPrefix []byte, depth int) {
if depth == len(builder)-1 {
// These are the "leaves", so append directly
for _, v := range builder[depth] {
out = append(out, append(currPrefix, v...))
}
return
}
for _, v := range builder[depth] {
nextPrefix := append(currPrefix, v...)
if c.AllowMissing {
out = append(out, nextPrefix)
}
walkVals(nextPrefix, depth+1)
}
}
walkVals(nil, 0)
return true, out, nil
}
func (c *CompoundMultiIndex) FromArgs(args ...interface{}) ([]byte, error) {
var stringMapCount int
var argCount int
for _, index := range c.Indexes {
if argCount >= len(args) {
break
}
if _, ok := index.(*StringMapFieldIndex); ok {
// We require pairs for StringMapFieldIndex, but only got one
if argCount+1 >= len(args) {
return nil, errors.New("invalid number of arguments")
}
stringMapCount++
argCount += 2
} else {
argCount++
}
}
argCount = 0
switch c.AllowMissing {
case true:
if len(args) > len(c.Indexes)+stringMapCount {
return nil, errors.New("too many arguments")
}
default:
if len(args) != len(c.Indexes)+stringMapCount {
return nil, errors.New("number of arguments does not equal number of indexers")
}
}
var out []byte
var val []byte
var err error
for i, idx := range c.Indexes {
if argCount >= len(args) {
// We're done; should only hit this if AllowMissing
break
}
if _, ok := idx.(*StringMapFieldIndex); ok {
if args[argCount+1] == nil {
val, err = idx.FromArgs(args[argCount])
} else {
val, err = idx.FromArgs(args[argCount : argCount+2]...)
}
argCount += 2
} else {
val, err = idx.FromArgs(args[argCount])
argCount++
}
if err != nil {
return nil, fmt.Errorf("sub-index %d error: %v", i, err)
}
out = append(out, val...)
}
return out, nil
}

View File

@ -7,7 +7,7 @@ import (
"sync/atomic" "sync/atomic"
"unsafe" "unsafe"
"github.com/hashicorp/go-immutable-radix" iradix "github.com/hashicorp/go-immutable-radix"
) )
const ( const (
@ -591,19 +591,11 @@ type ResultIterator interface {
// Get is used to construct a ResultIterator over all the // Get is used to construct a ResultIterator over all the
// rows that match the given constraints of an index. // rows that match the given constraints of an index.
func (txn *Txn) Get(table, index string, args ...interface{}) (ResultIterator, error) { func (txn *Txn) Get(table, index string, args ...interface{}) (ResultIterator, error) {
// Get the index value to scan indexIter, val, err := txn.getIndexIterator(table, index, args...)
indexSchema, val, err := txn.getIndexValue(table, index, args...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Get the index itself
indexTxn := txn.readableIndex(table, indexSchema.Name)
indexRoot := indexTxn.Root()
// Get an interator over the index
indexIter := indexRoot.Iterator()
// Seek the iterator to the appropriate sub-set // Seek the iterator to the appropriate sub-set
watchCh := indexIter.SeekPrefixWatch(val) watchCh := indexIter.SeekPrefixWatch(val)
@ -615,6 +607,44 @@ func (txn *Txn) Get(table, index string, args ...interface{}) (ResultIterator, e
return iter, nil return iter, nil
} }
// LowerBound is used to construct a ResultIterator over all the the range of
// rows that have an index value greater than or equal to the provide args.
// Calling this then iterating until the rows are larger than required allows
// range scans within an index. It is not possible to watch the resulting
// iterator since the radix tree doesn't efficiently allow watching on lower
// bound changes. The WatchCh returned will be nill and so will block forever.
func (txn *Txn) LowerBound(table, index string, args ...interface{}) (ResultIterator, error) {
indexIter, val, err := txn.getIndexIterator(table, index, args...)
if err != nil {
return nil, err
}
// Seek the iterator to the appropriate sub-set
indexIter.SeekLowerBound(val)
// Create an iterator
iter := &radixIterator{
iter: indexIter,
}
return iter, nil
}
func (txn *Txn) getIndexIterator(table, index string, args ...interface{}) (*iradix.Iterator, []byte, error) {
// Get the index value to scan
indexSchema, val, err := txn.getIndexValue(table, index, args...)
if err != nil {
return nil, nil, err
}
// Get the index itself
indexTxn := txn.readableIndex(table, indexSchema.Name)
indexRoot := indexTxn.Root()
// Get an interator over the index
indexIter := indexRoot.Iterator()
return indexIter, val, nil
}
// Defer is used to push a new arbitrary function onto a stack which // Defer is used to push a new arbitrary function onto a stack which
// gets called when a transaction is committed and finished. Deferred // gets called when a transaction is committed and finished. Deferred
// functions are called in LIFO order, and only invoked at the end of // functions are called in LIFO order, and only invoked at the end of

View File

@ -2,7 +2,7 @@ package memdb
//go:generate sh -c "go run watch-gen/main.go >watch_few.go" //go:generate sh -c "go run watch-gen/main.go >watch_few.go"
import( import (
"context" "context"
) )

4
vendor/modules.txt vendored
View File

@ -186,9 +186,9 @@ github.com/hashicorp/go-discover/provider/triton
github.com/hashicorp/go-discover/provider/vsphere github.com/hashicorp/go-discover/provider/vsphere
# github.com/hashicorp/go-hclog v0.9.2 # github.com/hashicorp/go-hclog v0.9.2
github.com/hashicorp/go-hclog github.com/hashicorp/go-hclog
# github.com/hashicorp/go-immutable-radix v1.0.0 # github.com/hashicorp/go-immutable-radix v1.1.0
github.com/hashicorp/go-immutable-radix github.com/hashicorp/go-immutable-radix
# github.com/hashicorp/go-memdb v0.0.0-20180223233045-1289e7fffe71 # github.com/hashicorp/go-memdb v1.0.3
github.com/hashicorp/go-memdb github.com/hashicorp/go-memdb
# github.com/hashicorp/go-msgpack v0.5.5 # github.com/hashicorp/go-msgpack v0.5.5
github.com/hashicorp/go-msgpack/codec github.com/hashicorp/go-msgpack/codec