From cc29b37bac57d0470704022e06e8549f97cf11b2 Mon Sep 17 00:00:00 2001 From: Paul Lorenz Date: Fri, 12 Jul 2024 15:29:41 -0400 Subject: [PATCH] Add support for external symbols to boltz stores. Fixes #76 --- boltz/base.go | 1 + boltz/external_symbol.go | 72 ++++++++++++++++++++++++++++++++++++++++ boltz/store.go | 3 +- boltz/store_query.go | 22 +++++++----- 4 files changed, 87 insertions(+), 11 deletions(-) create mode 100644 boltz/external_symbol.go diff --git a/boltz/base.go b/boltz/base.go index c172ce1..612a37f 100644 --- a/boltz/base.go +++ b/boltz/base.go @@ -173,6 +173,7 @@ type ConfigurableStore interface { AddPublicSetSymbol(name string, nodeType ast.NodeType) EntitySetSymbol AddFkSetSymbol(name string, linkedType Store) EntitySetSymbol NewEntitySymbol(name string, nodeType ast.NodeType) EntitySymbol + AddEntitySymbol(symbol EntitySymbol) AddExtEntitySymbols() MakeSymbolPublic(name string) diff --git a/boltz/external_symbol.go b/boltz/external_symbol.go new file mode 100644 index 0000000..c3da079 --- /dev/null +++ b/boltz/external_symbol.go @@ -0,0 +1,72 @@ +package boltz + +import ( + "github.com/openziti/storage/ast" + "go.etcd.io/bbolt" +) + +type ExternalSymbol struct { + store Store + name string + nodeType ast.NodeType + impl func(tx *bbolt.Tx, rowId []byte) (FieldType, []byte) +} + +func (self *ExternalSymbol) GetStore() Store { + return self.store +} + +func (self *ExternalSymbol) GetLinkedType() Store { + return nil +} + +func (self *ExternalSymbol) GetPath() []string { + return nil +} + +func (self *ExternalSymbol) GetType() ast.NodeType { + return self.nodeType +} + +func (self *ExternalSymbol) GetName() string { + return self.name +} + +func (self *ExternalSymbol) IsSet() bool { + return false +} + +func (self *ExternalSymbol) Eval(tx *bbolt.Tx, rowId []byte) (FieldType, []byte) { + return self.impl(tx, rowId) +} + +func NewBoolFuncSymbol(store Store, name string, f func(id string) bool) EntitySymbol { + return &ExternalSymbol{ + store: store, + name: name, + nodeType: ast.NodeTypeBool, + impl: func(tx *bbolt.Tx, rowId []byte) (FieldType, []byte) { + result := f(string(rowId)) + buf := make([]byte, 1) + if result { + buf[0] = 1 + } + return TypeBool, buf + }, + } +} + +func NewStringFuncSymbol(store Store, name string, f func(id string) *string) EntitySymbol { + return &ExternalSymbol{ + store: store, + name: name, + nodeType: ast.NodeTypeString, + impl: func(tx *bbolt.Tx, rowId []byte) (FieldType, []byte) { + result := f(string(rowId)) + if result == nil { + return TypeString, nil + } + return TypeString, []byte(*result) + }, + } +} diff --git a/boltz/store.go b/boltz/store.go index 4c3894b..f87738d 100644 --- a/boltz/store.go +++ b/boltz/store.go @@ -58,7 +58,6 @@ func NewBaseStore[E Entity](definition StoreDefinition[E]) *BaseStore[E] { entityStrategy: definition.EntityStrategy, parent: definition.Parent, parentMapper: definition.ParentMapper, - symbols: map[string]EntitySymbol{}, mapSymbols: map[string]*entityMapSymbol{}, publicSymbols: map[string]struct{}{}, Indexer: *NewIndexer(append(indexPath, IndexesBucket)...), @@ -204,7 +203,7 @@ type BaseStore[E Entity] struct { parentMapper func(childEntity Entity) Entity entityType string entityPath []string - symbols map[string]EntitySymbol + symbols concurrenz.CopyOnWriteMap[string, EntitySymbol] publicSymbols map[string]struct{} mapSymbols map[string]*entityMapSymbol isExtended bool diff --git a/boltz/store_query.go b/boltz/store_query.go index 5f7af57..234f365 100644 --- a/boltz/store_query.go +++ b/boltz/store_query.go @@ -86,7 +86,7 @@ func (store *BaseStore[E]) GetSymbol(name string) EntitySymbol { 4. Composite multi-value symbols (employee.directReports.phoneNumbers) 5. Maps (employee.tags.location, employee.manager.tags.location, employee.directReports.tags.location) */ - if result := store.symbols[name]; result != nil { + if result := store.symbols.Get(name); result != nil { // If it's a set symbol, make a runtime copy so we don't share cursor data. If we ever have a case where we // are evaluating the same symbol in multiple context, this will still break, but since currently any given // expression only involves a single symbol, this should not be a problem @@ -178,7 +178,7 @@ func (store *BaseStore[E]) createCompositeEntitySymbol(name string, first linked } func (store *BaseStore[E]) addSymbol(name string, public bool, symbol EntitySymbol) EntitySymbol { - store.symbols[name] = symbol + store.symbols.Put(name, symbol) if public { store.publicSymbols[name] = struct{}{} } @@ -190,7 +190,7 @@ func (store *BaseStore[E]) inheritMapSymbol(symbol *entityMapSymbol) { } func (store *BaseStore[E]) GrantSymbols(child ConfigurableStore) { - for name, value := range store.symbols { + for name, value := range store.symbols.AsMap() { child.addSymbol(name, store.IsPublicSymbol(name), value) } for name, value := range store.mapSymbols { @@ -210,11 +210,11 @@ func (store *BaseStore[E]) AddIdSymbol(name string, nodeType ast.NodeType) Entit } func (store *BaseStore[E]) MapSymbol(name string, mapper SymbolMapper) { - if symbol, found := store.symbols[name]; found { - store.symbols[name] = &symbolMapWrapper{ + if symbol := store.symbols.Get(name); symbol != nil { + store.symbols.Put(name, &symbolMapWrapper{ EntitySymbol: symbol, SymbolMapper: mapper, - } + }) } } @@ -242,6 +242,10 @@ func (store *BaseStore[E]) AddMapSymbol(name string, nodeType ast.NodeType, key } } +func (store *BaseStore[E]) AddEntitySymbol(symbol EntitySymbol) { + store.symbols.Put(symbol.GetName(), symbol) +} + func (store *BaseStore[E]) NewEntitySymbol(name string, nodeType ast.NodeType) EntitySymbol { return store.newEntitySymbol(name, nodeType, name, nil) } @@ -297,7 +301,7 @@ func (store *BaseStore[E]) addSetSymbol(name string, nodeType ast.NodeType, list result := &entitySetSymbolImpl{ entitySymbol: entitySymbol, } - store.symbols[name] = result + store.symbols.Put(name, result) return result } @@ -331,9 +335,9 @@ func (store *BaseStore[E]) newRowComparator(sort []ast.SortField) (RowComparator var symbolsComparators []symbolComparator for _, sortField := range sort { - symbol, found := store.symbols[sortField.Symbol()] + symbol := store.symbols.Get(sortField.Symbol()) forward := sortField.IsAscending() - if !found { + if symbol == nil { return nil, errors.Errorf("no such sort field: %v", sortField.Symbol()) } if symbol.IsSet() {