vendor: Update go-memdb dependency

This commit is contained in:
Kyle Havlovitz 2017-01-09 11:23:09 -08:00
parent aee766baba
commit 12da452136
No known key found for this signature in database
GPG key ID: 8A5E6B173056AD6C
5 changed files with 250 additions and 30 deletions

View file

@ -19,7 +19,7 @@ The database provides the following:
* Rich Indexing - Tables can support any number of indexes, which can be simple like * Rich Indexing - Tables can support any number of indexes, which can be simple like
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 reduces UUID can be efficiently compressed from strings into byte indexes for reduced
storage requirements. storage requirements.
For the underlying immutable radix trees, see [go-immutable-radix](https://github.com/hashicorp/go-immutable-radix). For the underlying immutable radix trees, see [go-immutable-radix](https://github.com/hashicorp/go-immutable-radix).

View file

@ -9,15 +9,27 @@ import (
// Indexer is an interface used for defining indexes // Indexer is an interface used for defining indexes
type Indexer interface { type Indexer interface {
// FromObject is used to extract an index value from an
// object or to indicate that the index value is missing.
FromObject(raw interface{}) (bool, []byte, error)
// ExactFromArgs is used to build an exact index lookup // ExactFromArgs is used to build an exact index lookup
// based on arguments // based on arguments
FromArgs(args ...interface{}) ([]byte, error) FromArgs(args ...interface{}) ([]byte, error)
} }
// SingleIndexer is an interface used for defining indexes
// generating a single entry per object
type SingleIndexer interface {
// FromObject is used to extract an index value from an
// object or to indicate that the index value is missing.
FromObject(raw interface{}) (bool, []byte, error)
}
// MultiIndexer is an interface used for defining indexes
// generating multiple entries per object
type MultiIndexer interface {
// FromObject is used to extract index values from an
// object or to indicate that the index value is missing.
FromObject(raw interface{}) (bool, [][]byte, error)
}
// PrefixIndexer can optionally be implemented for any // PrefixIndexer can optionally be implemented for any
// indexes that support prefix based iteration. This may // indexes that support prefix based iteration. This may
// not apply to all indexes. // not apply to all indexes.
@ -88,6 +100,155 @@ func (s *StringFieldIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) {
return val, nil return val, nil
} }
// StringSliceFieldIndex is used to extract a field from an object
// using reflection and builds an index on that field.
type StringSliceFieldIndex struct {
Field string
Lowercase bool
}
func (s *StringSliceFieldIndex) FromObject(obj interface{}) (bool, [][]byte, error) {
v := reflect.ValueOf(obj)
v = reflect.Indirect(v) // Dereference the pointer if any
fv := v.FieldByName(s.Field)
if !fv.IsValid() {
return false, nil,
fmt.Errorf("field '%s' for %#v is invalid", s.Field, obj)
}
if fv.Kind() != reflect.Slice || fv.Type().Elem().Kind() != reflect.String {
return false, nil, fmt.Errorf("field '%s' is not a string slice", s.Field)
}
length := fv.Len()
vals := make([][]byte, 0, length)
for i := 0; i < fv.Len(); i++ {
val := fv.Index(i).String()
if val == "" {
continue
}
if s.Lowercase {
val = strings.ToLower(val)
}
// Add the null character as a terminator
val += "\x00"
vals = append(vals, []byte(val))
}
if len(vals) == 0 {
return false, nil, nil
}
return true, vals, nil
}
func (s *StringSliceFieldIndex) FromArgs(args ...interface{}) ([]byte, error) {
if len(args) != 1 {
return nil, fmt.Errorf("must provide only a single argument")
}
arg, ok := args[0].(string)
if !ok {
return nil, fmt.Errorf("argument must be a string: %#v", args[0])
}
if s.Lowercase {
arg = strings.ToLower(arg)
}
// Add the null character as a terminator
arg += "\x00"
return []byte(arg), nil
}
func (s *StringSliceFieldIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) {
val, err := s.FromArgs(args...)
if err != nil {
return nil, err
}
// Strip the null terminator, the rest is a prefix
n := len(val)
if n > 0 {
return val[:n-1], nil
}
return val, nil
}
// StringMapFieldIndex is used to extract a field of type map[string]string
// from an object using reflection and builds an index on that field.
type StringMapFieldIndex struct {
Field string
Lowercase bool
}
var MapType = reflect.MapOf(reflect.TypeOf(""), reflect.TypeOf("")).Kind()
func (s *StringMapFieldIndex) FromObject(obj interface{}) (bool, [][]byte, error) {
v := reflect.ValueOf(obj)
v = reflect.Indirect(v) // Dereference the pointer if any
fv := v.FieldByName(s.Field)
if !fv.IsValid() {
return false, nil, fmt.Errorf("field '%s' for %#v is invalid", s.Field, obj)
}
if fv.Kind() != MapType {
return false, nil, fmt.Errorf("field '%s' is not a map[string]string", s.Field)
}
length := fv.Len()
vals := make([][]byte, 0, length)
for _, key := range fv.MapKeys() {
k := key.String()
if k == "" {
continue
}
val := fv.MapIndex(key).String()
if s.Lowercase {
k = strings.ToLower(k)
val = strings.ToLower(val)
}
// Add the null character as a terminator
k += "\x00" + val + "\x00"
vals = append(vals, []byte(k))
}
if len(vals) == 0 {
return false, nil, nil
}
return true, vals, nil
}
func (s *StringMapFieldIndex) FromArgs(args ...interface{}) ([]byte, error) {
if len(args) > 2 || len(args) == 0 {
return nil, fmt.Errorf("must provide one or two arguments")
}
key, ok := args[0].(string)
if !ok {
return nil, fmt.Errorf("argument must be a string: %#v", args[0])
}
if s.Lowercase {
key = strings.ToLower(key)
}
// Add the null character as a terminator
key += "\x00"
if len(args) == 2 {
val, ok := args[1].(string)
if !ok {
return nil, fmt.Errorf("argument must be a string: %#v", args[1])
}
if s.Lowercase {
val = strings.ToLower(val)
}
// Add the null character as a terminator
key += val + "\x00"
}
return []byte(key), nil
}
// UUIDFieldIndex is used to extract a field from an object // UUIDFieldIndex is used to extract a field from an object
// using reflection and builds an index on that field by treating // using reflection and builds an index on that field by treating
// it as a UUID. This is an optimization to using a StringFieldIndex // it as a UUID. This is an optimization to using a StringFieldIndex
@ -270,7 +431,11 @@ type CompoundIndex struct {
func (c *CompoundIndex) FromObject(raw interface{}) (bool, []byte, error) { func (c *CompoundIndex) FromObject(raw interface{}) (bool, []byte, error) {
var out []byte var out []byte
for i, idx := range c.Indexes { for i, idxRaw := range c.Indexes {
idx, ok := idxRaw.(SingleIndexer)
if !ok {
return false, nil, fmt.Errorf("sub-index %d error: %s", i, "sub-index must be a SingleIndexer")
}
ok, val, err := idx.FromObject(raw) ok, val, err := idx.FromObject(raw)
if err != nil { if err != nil {
return false, nil, fmt.Errorf("sub-index %d error: %v", i, err) return false, nil, fmt.Errorf("sub-index %d error: %v", i, err)

View file

@ -46,6 +46,9 @@ func (s *TableSchema) Validate() error {
if !s.Indexes["id"].Unique { if !s.Indexes["id"].Unique {
return fmt.Errorf("id index must be unique") return fmt.Errorf("id index must be unique")
} }
if _, ok := s.Indexes["id"].Indexer.(SingleIndexer); !ok {
return fmt.Errorf("id index must be a SingleIndexer")
}
for name, index := range s.Indexes { for name, index := range s.Indexes {
if name != index.Name { if name != index.Name {
return fmt.Errorf("index name mis-match for '%s'", name) return fmt.Errorf("index name mis-match for '%s'", name)
@ -72,5 +75,11 @@ func (s *IndexSchema) Validate() error {
if s.Indexer == nil { if s.Indexer == nil {
return fmt.Errorf("missing index function for '%s'", s.Name) return fmt.Errorf("missing index function for '%s'", s.Name)
} }
switch s.Indexer.(type) {
case SingleIndexer:
case MultiIndexer:
default:
return fmt.Errorf("indexer for '%s' must be a SingleIndexer or MultiIndexer", s.Name)
}
return nil return nil
} }

View file

@ -148,7 +148,8 @@ func (txn *Txn) Insert(table string, obj interface{}) error {
// Get the primary ID of the object // Get the primary ID of the object
idSchema := tableSchema.Indexes[id] idSchema := tableSchema.Indexes[id]
ok, idVal, err := idSchema.Indexer.FromObject(obj) idIndexer := idSchema.Indexer.(SingleIndexer)
ok, idVal, err := idIndexer.FromObject(obj)
if err != nil { if err != nil {
return fmt.Errorf("failed to build primary index: %v", err) return fmt.Errorf("failed to build primary index: %v", err)
} }
@ -167,7 +168,19 @@ func (txn *Txn) Insert(table string, obj interface{}) error {
indexTxn := txn.writableIndex(table, name) indexTxn := txn.writableIndex(table, name)
// Determine the new index value // Determine the new index value
ok, val, err := indexSchema.Indexer.FromObject(obj) var (
ok bool
vals [][]byte
err error
)
switch indexer := indexSchema.Indexer.(type) {
case SingleIndexer:
var val []byte
ok, val, err = indexer.FromObject(obj)
vals = [][]byte{val}
case MultiIndexer:
ok, vals, err = indexer.FromObject(obj)
}
if err != nil { if err != nil {
return fmt.Errorf("failed to build index '%s': %v", name, err) return fmt.Errorf("failed to build index '%s': %v", name, err)
} }
@ -176,28 +189,44 @@ func (txn *Txn) Insert(table string, obj interface{}) error {
// This is done by appending the primary key which must // This is done by appending the primary key which must
// be unique anyways. // be unique anyways.
if ok && !indexSchema.Unique { if ok && !indexSchema.Unique {
val = append(val, idVal...) for i := range vals {
vals[i] = append(vals[i], idVal...)
}
} }
// Handle the update by deleting from the index first // Handle the update by deleting from the index first
if update { if update {
okExist, valExist, err := indexSchema.Indexer.FromObject(existing) var (
okExist bool
valsExist [][]byte
err error
)
switch indexer := indexSchema.Indexer.(type) {
case SingleIndexer:
var valExist []byte
okExist, valExist, err = indexer.FromObject(existing)
valsExist = [][]byte{valExist}
case MultiIndexer:
okExist, valsExist, err = indexer.FromObject(existing)
}
if err != nil { if err != nil {
return fmt.Errorf("failed to build index '%s': %v", name, err) return fmt.Errorf("failed to build index '%s': %v", name, err)
} }
if okExist { if okExist {
// Handle non-unique index by computing a unique index. for i, valExist := range valsExist {
// This is done by appending the primary key which must // Handle non-unique index by computing a unique index.
// be unique anyways. // This is done by appending the primary key which must
if !indexSchema.Unique { // be unique anyways.
valExist = append(valExist, idVal...) if !indexSchema.Unique {
} valExist = append(valExist, idVal...)
}
// If we are writing to the same index with the same value, // If we are writing to the same index with the same value,
// we can avoid the delete as the insert will overwrite the // we can avoid the delete as the insert will overwrite the
// value anyways. // value anyways.
if !bytes.Equal(valExist, val) { if i >= len(vals) || !bytes.Equal(valExist, vals[i]) {
indexTxn.Delete(valExist) indexTxn.Delete(valExist)
}
} }
} }
} }
@ -213,7 +242,9 @@ func (txn *Txn) Insert(table string, obj interface{}) error {
} }
// Update the value of the index // Update the value of the index
indexTxn.Insert(val, obj) for _, val := range vals {
indexTxn.Insert(val, obj)
}
} }
return nil return nil
} }
@ -233,7 +264,8 @@ func (txn *Txn) Delete(table string, obj interface{}) error {
// Get the primary ID of the object // Get the primary ID of the object
idSchema := tableSchema.Indexes[id] idSchema := tableSchema.Indexes[id]
ok, idVal, err := idSchema.Indexer.FromObject(obj) idIndexer := idSchema.Indexer.(SingleIndexer)
ok, idVal, err := idIndexer.FromObject(obj)
if err != nil { if err != nil {
return fmt.Errorf("failed to build primary index: %v", err) return fmt.Errorf("failed to build primary index: %v", err)
} }
@ -253,7 +285,19 @@ func (txn *Txn) Delete(table string, obj interface{}) error {
indexTxn := txn.writableIndex(table, name) indexTxn := txn.writableIndex(table, name)
// Handle the update by deleting from the index first // Handle the update by deleting from the index first
ok, val, err := indexSchema.Indexer.FromObject(existing) var (
ok bool
vals [][]byte
err error
)
switch indexer := indexSchema.Indexer.(type) {
case SingleIndexer:
var val []byte
ok, val, err = indexer.FromObject(existing)
vals = [][]byte{val}
case MultiIndexer:
ok, vals, err = indexer.FromObject(existing)
}
if err != nil { if err != nil {
return fmt.Errorf("failed to build index '%s': %v", name, err) return fmt.Errorf("failed to build index '%s': %v", name, err)
} }
@ -261,10 +305,12 @@ func (txn *Txn) Delete(table string, obj interface{}) error {
// Handle non-unique index by computing a unique index. // Handle non-unique index by computing a unique index.
// This is done by appending the primary key which must // This is done by appending the primary key which must
// be unique anyways. // be unique anyways.
if !indexSchema.Unique { for _, val := range vals {
val = append(val, idVal...) if !indexSchema.Unique {
val = append(val, idVal...)
}
indexTxn.Delete(val)
} }
indexTxn.Delete(val)
} }
} }
return nil return nil

6
vendor/vendor.json vendored
View file

@ -396,10 +396,10 @@
"revisionTime": "2016-06-09T02:05:29Z" "revisionTime": "2016-06-09T02:05:29Z"
}, },
{ {
"checksumSHA1": "/V57CyN7x2NUlHoOzVL5GgGXX84=", "checksumSHA1": "ZpTDFeRvXFwIvSHRD8eDYHxaj4Y=",
"path": "github.com/hashicorp/go-memdb", "path": "github.com/hashicorp/go-memdb",
"revision": "98f52f52d7a476958fa9da671354d270c50661a7", "revision": "d2d2b77acab85aa635614ac17ea865969f56009e",
"revisionTime": "2016-03-01T23:01:42Z" "revisionTime": "2017-01-07T16:22:14Z"
}, },
{ {
"checksumSHA1": "TNlVzNR1OaajcNi3CbQ3bGbaLGU=", "checksumSHA1": "TNlVzNR1OaajcNi3CbQ3bGbaLGU=",