vendor: Update go-memdb dependency
This commit is contained in:
parent
aee766baba
commit
12da452136
|
@ -19,7 +19,7 @@ The database provides the following:
|
|||
|
||||
* 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
|
||||
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.
|
||||
|
||||
For the underlying immutable radix trees, see [go-immutable-radix](https://github.com/hashicorp/go-immutable-radix).
|
||||
|
|
|
@ -9,15 +9,27 @@ import (
|
|||
|
||||
// Indexer is an interface used for defining indexes
|
||||
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
|
||||
// based on arguments
|
||||
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
|
||||
// indexes that support prefix based iteration. This may
|
||||
// not apply to all indexes.
|
||||
|
@ -88,6 +100,155 @@ func (s *StringFieldIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) {
|
|||
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
|
||||
// using reflection and builds an index on that field by treating
|
||||
// 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) {
|
||||
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)
|
||||
if err != nil {
|
||||
return false, nil, fmt.Errorf("sub-index %d error: %v", i, err)
|
||||
|
|
|
@ -46,6 +46,9 @@ func (s *TableSchema) Validate() error {
|
|||
if !s.Indexes["id"].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 {
|
||||
if name != index.Name {
|
||||
return fmt.Errorf("index name mis-match for '%s'", name)
|
||||
|
@ -72,5 +75,11 @@ func (s *IndexSchema) Validate() error {
|
|||
if s.Indexer == nil {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -148,7 +148,8 @@ func (txn *Txn) Insert(table string, obj interface{}) error {
|
|||
|
||||
// Get the primary ID of the object
|
||||
idSchema := tableSchema.Indexes[id]
|
||||
ok, idVal, err := idSchema.Indexer.FromObject(obj)
|
||||
idIndexer := idSchema.Indexer.(SingleIndexer)
|
||||
ok, idVal, err := idIndexer.FromObject(obj)
|
||||
if err != nil {
|
||||
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)
|
||||
|
||||
// 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 {
|
||||
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
|
||||
// be unique anyways.
|
||||
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
|
||||
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 {
|
||||
return fmt.Errorf("failed to build index '%s': %v", name, err)
|
||||
}
|
||||
if okExist {
|
||||
// Handle non-unique index by computing a unique index.
|
||||
// This is done by appending the primary key which must
|
||||
// be unique anyways.
|
||||
if !indexSchema.Unique {
|
||||
valExist = append(valExist, idVal...)
|
||||
}
|
||||
for i, valExist := range valsExist {
|
||||
// Handle non-unique index by computing a unique index.
|
||||
// This is done by appending the primary key which must
|
||||
// be unique anyways.
|
||||
if !indexSchema.Unique {
|
||||
valExist = append(valExist, idVal...)
|
||||
}
|
||||
|
||||
// If we are writing to the same index with the same value,
|
||||
// we can avoid the delete as the insert will overwrite the
|
||||
// value anyways.
|
||||
if !bytes.Equal(valExist, val) {
|
||||
indexTxn.Delete(valExist)
|
||||
// If we are writing to the same index with the same value,
|
||||
// we can avoid the delete as the insert will overwrite the
|
||||
// value anyways.
|
||||
if i >= len(vals) || !bytes.Equal(valExist, vals[i]) {
|
||||
indexTxn.Delete(valExist)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -213,7 +242,9 @@ func (txn *Txn) Insert(table string, obj interface{}) error {
|
|||
}
|
||||
|
||||
// Update the value of the index
|
||||
indexTxn.Insert(val, obj)
|
||||
for _, val := range vals {
|
||||
indexTxn.Insert(val, obj)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -233,7 +264,8 @@ func (txn *Txn) Delete(table string, obj interface{}) error {
|
|||
|
||||
// Get the primary ID of the object
|
||||
idSchema := tableSchema.Indexes[id]
|
||||
ok, idVal, err := idSchema.Indexer.FromObject(obj)
|
||||
idIndexer := idSchema.Indexer.(SingleIndexer)
|
||||
ok, idVal, err := idIndexer.FromObject(obj)
|
||||
if err != nil {
|
||||
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)
|
||||
|
||||
// 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 {
|
||||
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.
|
||||
// This is done by appending the primary key which must
|
||||
// be unique anyways.
|
||||
if !indexSchema.Unique {
|
||||
val = append(val, idVal...)
|
||||
for _, val := range vals {
|
||||
if !indexSchema.Unique {
|
||||
val = append(val, idVal...)
|
||||
}
|
||||
indexTxn.Delete(val)
|
||||
}
|
||||
indexTxn.Delete(val)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -396,10 +396,10 @@
|
|||
"revisionTime": "2016-06-09T02:05:29Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "/V57CyN7x2NUlHoOzVL5GgGXX84=",
|
||||
"checksumSHA1": "ZpTDFeRvXFwIvSHRD8eDYHxaj4Y=",
|
||||
"path": "github.com/hashicorp/go-memdb",
|
||||
"revision": "98f52f52d7a476958fa9da671354d270c50661a7",
|
||||
"revisionTime": "2016-03-01T23:01:42Z"
|
||||
"revision": "d2d2b77acab85aa635614ac17ea865969f56009e",
|
||||
"revisionTime": "2017-01-07T16:22:14Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "TNlVzNR1OaajcNi3CbQ3bGbaLGU=",
|
||||
|
|
Loading…
Reference in New Issue