mirror of
https://github.com/safing/portbase
synced 2025-09-01 18:19:57 +00:00
Add GetMeta to database storage interface
This commit is contained in:
parent
3f3786b854
commit
0061572e1b
11 changed files with 170 additions and 32 deletions
|
@ -42,7 +42,7 @@ func (c *Controller) Injected() bool {
|
||||||
return c.storage.Injected()
|
return c.storage.Injected()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get return the record with the given key.
|
// Get returns the record with the given key.
|
||||||
func (c *Controller) Get(key string) (record.Record, error) {
|
func (c *Controller) Get(key string) (record.Record, error) {
|
||||||
if shuttingDown.IsSet() {
|
if shuttingDown.IsSet() {
|
||||||
return nil, ErrShuttingDown
|
return nil, ErrShuttingDown
|
||||||
|
@ -76,6 +76,28 @@ func (c *Controller) Get(key string) (record.Record, error) {
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get returns the metadata of the record with the given key.
|
||||||
|
func (c *Controller) GetMeta(key string) (*record.Meta, error) {
|
||||||
|
if shuttingDown.IsSet() {
|
||||||
|
return nil, ErrShuttingDown
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := c.storage.GetMeta(key)
|
||||||
|
if err != nil {
|
||||||
|
// replace not found error
|
||||||
|
if err == storage.ErrNotFound {
|
||||||
|
return nil, ErrNotFound
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !m.CheckValidity() {
|
||||||
|
return nil, ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Put saves a record in the database, executes any registered
|
// Put saves a record in the database, executes any registered
|
||||||
// pre-put hooks and finally send an update to all subscribers.
|
// pre-put hooks and finally send an update to all subscribers.
|
||||||
// The record must be locked and secured from concurrent access
|
// The record must be locked and secured from concurrent access
|
||||||
|
|
|
@ -201,6 +201,40 @@ func (i *Interface) getRecord(dbName string, dbKey string, mustBeWriteable bool)
|
||||||
return r, db, nil
|
return r, db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Interface) getMeta(dbName string, dbKey string, mustBeWriteable bool) (m *record.Meta, db *Controller, err error) {
|
||||||
|
if dbName == "" {
|
||||||
|
dbName, dbKey = record.ParseKey(dbKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err = getController(dbName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if mustBeWriteable && db.ReadOnly() {
|
||||||
|
return nil, db, ErrReadOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
r := i.checkCache(dbName + ":" + dbKey)
|
||||||
|
if r != nil {
|
||||||
|
if !i.options.hasAccessPermission(r) {
|
||||||
|
return nil, db, ErrPermissionDenied
|
||||||
|
}
|
||||||
|
return r.Meta(), db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err = db.GetMeta(dbKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, db, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !m.CheckPermission(i.options.Local, i.options.Internal) {
|
||||||
|
return nil, db, ErrPermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, db, nil
|
||||||
|
}
|
||||||
|
|
||||||
// InsertValue inserts a value into a record.
|
// InsertValue inserts a value into a record.
|
||||||
func (i *Interface) InsertValue(key string, attribute string, value interface{}) error {
|
func (i *Interface) InsertValue(key string, attribute string, value interface{}) error {
|
||||||
r, db, err := i.getRecord(getDBFromKey, key, true)
|
r, db, err := i.getRecord(getDBFromKey, key, true)
|
||||||
|
@ -236,7 +270,7 @@ func (i *Interface) Put(r record.Record) (err error) {
|
||||||
// get record or only database
|
// get record or only database
|
||||||
var db *Controller
|
var db *Controller
|
||||||
if !i.options.HasAllPermissions() {
|
if !i.options.HasAllPermissions() {
|
||||||
_, db, err = i.getRecord(r.DatabaseName(), r.DatabaseKey(), true)
|
_, db, err = i.getMeta(r.DatabaseName(), r.DatabaseKey(), true)
|
||||||
if err != nil && err != ErrNotFound {
|
if err != nil && err != ErrNotFound {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -247,7 +281,7 @@ func (i *Interface) Put(r record.Record) (err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if database is read only before we add to the cache.
|
// Check if database is read only.
|
||||||
if db.ReadOnly() {
|
if db.ReadOnly() {
|
||||||
return ErrReadOnly
|
return ErrReadOnly
|
||||||
}
|
}
|
||||||
|
@ -274,7 +308,7 @@ func (i *Interface) PutNew(r record.Record) (err error) {
|
||||||
// get record or only database
|
// get record or only database
|
||||||
var db *Controller
|
var db *Controller
|
||||||
if !i.options.HasAllPermissions() {
|
if !i.options.HasAllPermissions() {
|
||||||
_, db, err = i.getRecord(r.DatabaseName(), r.DatabaseKey(), true)
|
_, db, err = i.getMeta(r.DatabaseName(), r.DatabaseKey(), true)
|
||||||
if err != nil && err != ErrNotFound {
|
if err != nil && err != ErrNotFound {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -285,6 +319,11 @@ func (i *Interface) PutNew(r record.Record) (err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if database is read only.
|
||||||
|
if db.ReadOnly() {
|
||||||
|
return ErrReadOnly
|
||||||
|
}
|
||||||
|
|
||||||
r.Lock()
|
r.Lock()
|
||||||
if r.Meta() != nil {
|
if r.Meta() != nil {
|
||||||
r.Meta().Reset()
|
r.Meta().Reset()
|
||||||
|
@ -328,6 +367,13 @@ func (i *Interface) PutMany(dbName string) (put func(record.Record) error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if database is read only.
|
||||||
|
if db.ReadOnly() {
|
||||||
|
return func(r record.Record) error {
|
||||||
|
return ErrReadOnly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// start database access
|
// start database access
|
||||||
dbBatch, errs := db.PutMany()
|
dbBatch, errs := db.PutMany()
|
||||||
finished := abool.New()
|
finished := abool.New()
|
||||||
|
@ -462,6 +508,11 @@ func (i *Interface) Delete(key string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if database is read only.
|
||||||
|
if db.ReadOnly() {
|
||||||
|
return ErrReadOnly
|
||||||
|
}
|
||||||
|
|
||||||
i.options.Apply(r)
|
i.options.Apply(r)
|
||||||
r.Meta().Delete()
|
r.Meta().Delete()
|
||||||
return db.Put(r)
|
return db.Put(r)
|
||||||
|
@ -495,6 +546,11 @@ func (i *Interface) Purge(ctx context.Context, q *query.Query) (int, error) {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if database is read only before we add to the cache.
|
||||||
|
if db.ReadOnly() {
|
||||||
|
return 0, ErrReadOnly
|
||||||
|
}
|
||||||
|
|
||||||
return db.Purge(ctx, q, i.options.Local, i.options.Internal)
|
return db.Purge(ctx, q, i.options.Local, i.options.Internal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ Please note that some feeders may have other special characters. It is advised t
|
||||||
## Operators
|
## Operators
|
||||||
|
|
||||||
| Name | Textual | Req. Type | Internal Type | Compared with |
|
| Name | Textual | Req. Type | Internal Type | Compared with |
|
||||||
|---|---|---|---|
|
|-------------------------|--------------------|-----------|---------------|---------------------------|
|
||||||
| Equals | `==` | int | int64 | `==` |
|
| Equals | `==` | int | int64 | `==` |
|
||||||
| GreaterThan | `>` | int | int64 | `>` |
|
| GreaterThan | `>` | int | int64 | `>` |
|
||||||
| GreaterThanOrEqual | `>=` | int | int64 | `>=` |
|
| GreaterThanOrEqual | `>=` | int | int64 | `>=` |
|
||||||
|
@ -38,11 +38,11 @@ Please note that some feeders may have other special characters. It is advised t
|
||||||
| StartsWith | `startswith`, `sw` | string | string | `strings.HasPrefix()` |
|
| StartsWith | `startswith`, `sw` | string | string | `strings.HasPrefix()` |
|
||||||
| EndsWith | `endswith`, `ew` | string | string | `strings.HasSuffix()` |
|
| EndsWith | `endswith`, `ew` | string | string | `strings.HasSuffix()` |
|
||||||
| In | `in` | string | string | for loop with `==` |
|
| In | `in` | string | string | for loop with `==` |
|
||||||
| Matches | `matches`, `re` | string | int64 | `regexp.Regexp.Matches()` |
|
| Matches | `matches`, `re` | string | string | `regexp.Regexp.Matches()` |
|
||||||
| Is | `is` | bool* | bool | `==` |
|
| Is | `is` | bool* | bool | `==` |
|
||||||
| Exists | `exists`, `ex` | any | n/a | n/a |
|
| Exists | `exists`, `ex` | any | n/a | n/a |
|
||||||
|
|
||||||
\*accepts strings: 1, t, T, TRUE, true, True, 0, f, F, FALSE
|
\*accepts strings: 1, t, T, true, True, TRUE, 0, f, F, false, False, FALSE
|
||||||
|
|
||||||
## Escaping
|
## Escaping
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ var (
|
||||||
registry = make(map[string]*Database)
|
registry = make(map[string]*Database)
|
||||||
registryLock sync.Mutex
|
registryLock sync.Mutex
|
||||||
|
|
||||||
nameConstraint = regexp.MustCompile("^[A-Za-z0-9_-]{4,}$")
|
nameConstraint = regexp.MustCompile("^[A-Za-z0-9_-]{3,}$")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Register registers a new database.
|
// Register registers a new database.
|
||||||
|
@ -56,7 +56,7 @@ func Register(new *Database) (*Database, error) {
|
||||||
} else {
|
} else {
|
||||||
// register new database
|
// register new database
|
||||||
if !nameConstraint.MatchString(new.Name) {
|
if !nameConstraint.MatchString(new.Name) {
|
||||||
return nil, errors.New("database name must only contain alphanumeric and `_-` characters and must be at least 4 characters long")
|
return nil, errors.New("database name must only contain alphanumeric and `_-` characters and must be at least 3 characters long")
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now().Round(time.Second)
|
now := time.Now().Round(time.Second)
|
||||||
|
|
|
@ -82,6 +82,18 @@ func (b *Badger) Get(key string) (record.Record, error) {
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMeta returns the metadata of a database record.
|
||||||
|
func (b *Badger) GetMeta(key string) (*record.Meta, error) {
|
||||||
|
// TODO: Replace with more performant variant.
|
||||||
|
|
||||||
|
r, err := b.Get(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Meta(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Put stores a record in the database.
|
// Put stores a record in the database.
|
||||||
func (b *Badger) Put(r record.Record) (record.Record, error) {
|
func (b *Badger) Put(r record.Record) (record.Record, error) {
|
||||||
data, err := r.MarshalRecord(r)
|
data, err := r.MarshalRecord(r)
|
||||||
|
|
|
@ -96,6 +96,18 @@ func (b *BBolt) Get(key string) (record.Record, error) {
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMeta returns the metadata of a database record.
|
||||||
|
func (b *BBolt) GetMeta(key string) (*record.Meta, error) {
|
||||||
|
// TODO: Replace with more performant variant.
|
||||||
|
|
||||||
|
r, err := b.Get(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Meta(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Put stores a record in the database.
|
// Put stores a record in the database.
|
||||||
func (b *BBolt) Put(r record.Record) (record.Record, error) {
|
func (b *BBolt) Put(r record.Record) (record.Record, error) {
|
||||||
data, err := r.MarshalRecord(r)
|
data, err := r.MarshalRecord(r)
|
||||||
|
|
|
@ -104,6 +104,18 @@ func (fst *FSTree) Get(key string) (record.Record, error) {
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMeta returns the metadata of a database record.
|
||||||
|
func (fst *FSTree) GetMeta(key string) (*record.Meta, error) {
|
||||||
|
// TODO: Replace with more performant variant.
|
||||||
|
|
||||||
|
r, err := fst.Get(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Meta(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Put stores a record in the database.
|
// Put stores a record in the database.
|
||||||
func (fst *FSTree) Put(r record.Record) (record.Record, error) {
|
func (fst *FSTree) Put(r record.Record) (record.Record, error) {
|
||||||
dstPath, err := fst.buildFilePath(r.DatabaseKey(), true)
|
dstPath, err := fst.buildFilePath(r.DatabaseKey(), true)
|
||||||
|
|
|
@ -44,6 +44,18 @@ func (hm *HashMap) Get(key string) (record.Record, error) {
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMeta returns the metadata of a database record.
|
||||||
|
func (hm *HashMap) GetMeta(key string) (*record.Meta, error) {
|
||||||
|
// TODO: Replace with more performant variant.
|
||||||
|
|
||||||
|
r, err := hm.Get(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Meta(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Put stores a record in the database.
|
// Put stores a record in the database.
|
||||||
func (hm *HashMap) Put(r record.Record) (record.Record, error) {
|
func (hm *HashMap) Put(r record.Record) (record.Record, error) {
|
||||||
hm.dbLock.Lock()
|
hm.dbLock.Lock()
|
||||||
|
|
|
@ -11,7 +11,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errNotImplemented = errors.New("not implemented")
|
// ErrNotImplemented is returned when a function is not implemented by a storage.
|
||||||
|
ErrNotImplemented = errors.New("not implemented")
|
||||||
)
|
)
|
||||||
|
|
||||||
// InjectBase is a dummy base structure to reduce boilerplate code for injected storage interfaces.
|
// InjectBase is a dummy base structure to reduce boilerplate code for injected storage interfaces.
|
||||||
|
@ -22,22 +23,27 @@ var _ Interface = &InjectBase{}
|
||||||
|
|
||||||
// Get returns a database record.
|
// Get returns a database record.
|
||||||
func (i *InjectBase) Get(key string) (record.Record, error) {
|
func (i *InjectBase) Get(key string) (record.Record, error) {
|
||||||
return nil, errNotImplemented
|
return nil, ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a database record.
|
||||||
|
func (i *InjectBase) GetMeta(key string) (*record.Meta, error) {
|
||||||
|
return nil, ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put stores a record in the database.
|
// Put stores a record in the database.
|
||||||
func (i *InjectBase) Put(m record.Record) (record.Record, error) {
|
func (i *InjectBase) Put(m record.Record) (record.Record, error) {
|
||||||
return nil, errNotImplemented
|
return nil, ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete deletes a record from the database.
|
// Delete deletes a record from the database.
|
||||||
func (i *InjectBase) Delete(key string) error {
|
func (i *InjectBase) Delete(key string) error {
|
||||||
return errNotImplemented
|
return ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query returns a an iterator for the supplied query.
|
// Query returns a an iterator for the supplied query.
|
||||||
func (i *InjectBase) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) {
|
func (i *InjectBase) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) {
|
||||||
return nil, errNotImplemented
|
return nil, ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadOnly returns whether the database is read only.
|
// ReadOnly returns whether the database is read only.
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
type Interface interface {
|
type Interface interface {
|
||||||
// Primary Interface
|
// Primary Interface
|
||||||
Get(key string) (record.Record, error)
|
Get(key string) (record.Record, error)
|
||||||
|
GetMeta(key string) (*record.Meta, error)
|
||||||
Put(m record.Record) (record.Record, error)
|
Put(m record.Record) (record.Record, error)
|
||||||
Delete(key string) error
|
Delete(key string) error
|
||||||
Query(q *query.Query, local, internal bool) (*iterator.Iterator, error)
|
Query(q *query.Query, local, internal bool) (*iterator.Iterator, error)
|
||||||
|
|
|
@ -44,6 +44,11 @@ func (s *Sinkhole) Get(key string) (record.Record, error) {
|
||||||
return nil, storage.ErrNotFound
|
return nil, storage.ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMeta returns the metadata of a database record.
|
||||||
|
func (s *Sinkhole) GetMeta(key string) (*record.Meta, error) {
|
||||||
|
return nil, storage.ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
// Put stores a record in the database.
|
// Put stores a record in the database.
|
||||||
func (s *Sinkhole) Put(r record.Record) (record.Record, error) {
|
func (s *Sinkhole) Put(r record.Record) (record.Record, error) {
|
||||||
return r, nil
|
return r, nil
|
||||||
|
|
Loading…
Add table
Reference in a new issue