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
|
* 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).
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,16 +189,31 @@ 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 {
|
||||||
|
for i, valExist := range valsExist {
|
||||||
// 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.
|
||||||
|
@ -196,11 +224,12 @@ func (txn *Txn) Insert(table string, obj interface{}) error {
|
||||||
// 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If there is no index value, either this is an error or an expected
|
// If there is no index value, either this is an error or an expected
|
||||||
// case and we can skip updating
|
// case and we can skip updating
|
||||||
|
@ -213,8 +242,10 @@ func (txn *Txn) Insert(table string, obj interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the value of the index
|
// Update the value of the index
|
||||||
|
for _, val := range vals {
|
||||||
indexTxn.Insert(val, obj)
|
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,12 +305,14 @@ 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.
|
||||||
|
for _, val := range vals {
|
||||||
if !indexSchema.Unique {
|
if !indexSchema.Unique {
|
||||||
val = append(val, idVal...)
|
val = append(val, idVal...)
|
||||||
}
|
}
|
||||||
indexTxn.Delete(val)
|
indexTxn.Delete(val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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=",
|
||||||
|
|
Loading…
Reference in New Issue