Documentation ¶
Overview ¶
The veloci-filter (vfilter) library implements a generic SQL like query language.
Overview::
There are many applications in which it is useful to provide a flexible query language for the end user. Velocifilter has the following design goals:
- It should be generic and easily adaptable to be used by any project.
- It should be fast and efficient.
An example makes the use case very clear. Suppose you are writing an archiving application. Most archiving tools require a list of files to be archived (e.g. on the command line).
You launch your tool and a user requests a new flag that allows them to specify the files using a glob expression. For example, a user might wish to only select the files ending with the ".go" extension. While on a unix system one might use shell expansion to support this, on other operating systems shell expansion may not work (e.g. on windows).
You then add the ability to specify a glob expression directly to your tool (suppose you add the flag --glob). A short while later, a user requires filtering the files to archive by their size - suppose they want to only archive a file smaller than a certain size. You studiously add another set of flags (e.g. --size with a special syntax for greater than or less than semantics).
Now a user wishes to be able to combine these conditions logically (e.g. all files with ".go" extension newer than 5 days and smaller than 5kb).
Clearly this approach is limited, if we wanted to support every possible use case, our tool would add many flags with a complex syntax making it harder for our users. One approach is to simply rely on the unix "find" tool (with its many obscure flags) to support the file selection problem. This is not ideal either since the find tool may not be present on the system (E.g. on Windows) or may have varying syntax. It may also not support every possible condition the user may have in mind (e.g. files containing a RegExp or files not present in the archive).
There has to be a better way. You wish to provide your users with a powerful and flexible way to specify which files to archive, but we do not want to write complicated logic and make our tool more complex to use.
This is where velocifilter comes in. By using the library we can provide a single flag where the user may specify a flexible VQL query (Velocidex Query Language - a simplified SQL dialect) allowing the user to specify arbirarily complex filter expressions. For example:
SELECT file from glob(pattern=["*.go", "*.py"]) where file.Size < 5000 and file.Mtime < now() - "5 days"
Not only does VQL allow for complex logical operators, but it is also efficient and optimized automatically. For example, consider the following query:
SELECT file from glob(pattern="*") where grep(file=file, pattern="foobar") and file.Size < 5k
The grep() function will open the file and search it for the pattern. If the file is large, this might take a long time. However velocifilter will automatically abort the grep() function if the file size is larger than 5k bytes. Velocifilter correctly handles such cancellations automatically in order to reduce query evaluation latency.
Protocols - supporting custom types::
Velocifilter uses a plugin system to allow clients to define how their own custom types behave within the VQL evaluator.
Note that this is necessary because Go does not allow an external package to add an interface to an existing type without creating a new type which embeds it. Clients who need to handle the original third party types must have a way to attach new protocols to existing types defined outside their own codebase. Velocifilter achieves this by implementing a registration systen in the Scope{} object.
For example, consider a client of the library wishing to pass custom types in queries:
type Foo struct { ... bar Bar }
Where both Foo and Bar are defined and produced by some other library which our client uses. Suppose our client wishes to allow addition of Foo objects. We would therefore need to implement the AddProtocol interface on Foo structs. Since Foo structs are defined externally we can not simply add a new method to Foo struct (we could embed Foo struct in a new struct, but then we would also need to wrap the bar field to produce an extended Bar. This is typically impractical and not maintainable for heavily nested complex structs). We define a FooAdder{} object which implements the Addition protocol on behalf of the Foo object.
// This is an object which implements addition between two Foo objects. type FooAdder struct{} // This method will be run to see if this implementation is // applicable. We only want to run when we add two Foo objects together. func (self FooAdder) Applicable(a Any, b Any) bool { _, a_ok := a.(Foo) _, b_ok := b.(Foo) return a_ok && b_ok } // Actually implement the addition between two Foo objects. func (self FooAdder) Add(scope types.Scope, a Any, b Any) Any { ... return new object (does not have to be Foo{}). }
Now clients can add this protocol to the scope before evaluating a query:
scope := NewScope().AddProtocolImpl(FooAdder{})
Index ¶
- Variables
- func CopyFunction(in types.Any) types.FunctionInterface
- func FormatToString(scope types.Scope, node interface{}) string
- func GetIntScope(scope_int types.Scope) *scope.Scope
- func GetResponseChannel(vql *VQL, ctx context.Context, scope types.Scope, encoder RowEncoder, ...) <-chan *VFilterJsonResult
- func MaterializedLazyRow(ctx context.Context, row Row, scope types.Scope) *ordereddict.Dict
- func NewLazyExpr(ctx context.Context, scope types.Scope, expr *_AndExpression) types.LazyExpr
- func NewScope() types.Scope
- func NewStoredQuery(query *_Select, name string) *_StoredQuery
- func NewTimeThrottler(rate float64) types.Throttler
- func NewUnmarshaller(ignore_vars []string) *marshal.Unmarshaller
- func OutputJSON(vql *VQL, ctx context.Context, scope types.Scope, encoder RowEncoder) ([]byte, error)
- func RowToDict(ctx context.Context, scope types.Scope, row types.Row) *ordereddict.Dict
- type Any
- type Empty
- type FormatOptions
- type FunctionInfo
- type FunctionInterface
- type GenericFunction
- type GenericListPlugin
- type GroupbyActor
- func (self *GroupbyActor) GetNextRow(ctx context.Context, scope types.Scope) (types.LazyRow, types.Row, string, types.Scope, error)
- func (self *GroupbyActor) MaterializeRow(ctx context.Context, row types.Row, scope types.Scope) *ordereddict.Dict
- func (self *GroupbyActor) Transform(ctx context.Context, scope types.Scope, row types.Row) (types.LazyRow, func())
- type Lambda
- type LazyExpr
- type LazyExprImpl
- type LazyRowImpl
- type MultiVQL
- type Null
- type OrdereddictUnmarshaller
- type Plugin
- type PluginGeneratorInterface
- type PluginInfo
- type ReplayUnmarshaller
- type Row
- type RowEncoder
- type Scope
- type ScopeUnmarshaller
- type StoredExpression
- type StoredQuery
- type StoredQueryCallSite
- type StoredQueryItem
- type TimeThrottler
- type TypeMap
- type VFilterJsonResult
- type VQL
- type Visitor
Constants ¶
This section is empty.
Variables ¶
var ( ToStringOptions = FormatOptions{ BreakLines: false, MaxWidthThreshold: 1000000, } DefaultFormatOptions = FormatOptions{ IndentWidthThreshold: 50, MaxWidthThreshold: 80, ArgsOnNewLine: true, BreakLines: true, } )
Functions ¶
func CopyFunction ¶
func CopyFunction(in types.Any) types.FunctionInterface
func FormatToString ¶
func GetResponseChannel ¶
func GetResponseChannel( vql *VQL, ctx context.Context, scope types.Scope, encoder RowEncoder, maxrows int, max_wait int) <-chan *VFilterJsonResult
Returns a channel over which multi part results are sent.
func MaterializedLazyRow ¶
Takes a row returned from a plugin and materialize it into basic types. Generally this should only be LazyRow as this is only called from the Transformer. NOTE: This function only materialized the columns - it does not recursively materialize all objects.
func NewLazyExpr ¶
func NewStoredQuery ¶
func NewStoredQuery(query *_Select, name string) *_StoredQuery
func NewTimeThrottler ¶
func NewUnmarshaller ¶
func NewUnmarshaller(ignore_vars []string) *marshal.Unmarshaller
func OutputJSON ¶
func OutputJSON( vql *VQL, ctx context.Context, scope types.Scope, encoder RowEncoder) ([]byte, error)
A convenience function to generate JSON output from a VQL query.
Types ¶
type FormatOptions ¶
type FunctionInfo ¶
type FunctionInfo = types.FunctionInfo
type FunctionInterface ¶
type FunctionInterface = types.FunctionInterface
type GenericFunction ¶
type GenericFunction = functions.GenericFunction
type GenericListPlugin ¶
type GenericListPlugin = plugins.GenericListPlugin
type GroupbyActor ¶
type GroupbyActor struct {
// contains filtered or unexported fields
}
func (*GroupbyActor) GetNextRow ¶
func (self *GroupbyActor) GetNextRow(ctx context.Context, scope types.Scope) ( types.LazyRow, types.Row, string, types.Scope, error)
Pull the next row off the query possibly filtering it. This function adds the row to a new child scope, which the caller must close.
func (*GroupbyActor) MaterializeRow ¶
func (self *GroupbyActor) MaterializeRow(ctx context.Context, row types.Row, scope types.Scope) *ordereddict.Dict
type Lambda ¶
type Lambda struct { Parameters *_ParameterList ` @@ ` LetOperator string ` @"=>" ` Expression *_AndExpression ` @@ ` }
func ParseLambda ¶
func (*Lambda) GetParameters ¶
type LazyExprImpl ¶
type LazyExprImpl struct { Value types.Any // Used to cache Expr *_AndExpression // contains filtered or unexported fields }
A LazyExpr may be passed into a plugin arg for later evaluation. The plugin may completely ignore the expression and so will not evaluate it at all. Once evaluated LazyExpr will cache the value and can be used again. NOTE that LazyExpr is used purely for caching and so it uses the local scope (at the point of definition) to evaluate the expression - not the scope at the point of reference!
func (*LazyExprImpl) ReduceWithScope ¶
type LazyRowImpl ¶
type LazyRowImpl struct {
// contains filtered or unexported fields
}
A LazyRow holds callbacks as columns. When a column is accessed, the LazyRow will call the callback to materialize it, then cache the results. LazyRows are used to avoid calling expensive functions when the query does not need them - LazyRows are created in the SELECT transformer to delay evaluation of column specifiers until they are accessed.
func NewLazyRow ¶
func NewLazyRow(ctx context.Context, scope types.Scope) *LazyRowImpl
func (*LazyRowImpl) Columns ¶
func (self *LazyRowImpl) Columns() []string
func (*LazyRowImpl) Has ¶
func (self *LazyRowImpl) Has(key string) bool
type MultiVQL ¶
type MultiVQL struct { Comments []*_Comment `{ @@ } ` VQL1 *VQL ` @@ ` Comments2 []*_Comment `{ @@ } ` VQL2 *MultiVQL ` { @@ } ` }
func (*MultiVQL) GetStatements ¶
type OrdereddictUnmarshaller ¶
type OrdereddictUnmarshaller struct{}
func (OrdereddictUnmarshaller) Unmarshal ¶
func (self OrdereddictUnmarshaller) Unmarshal( unmarshaller types.Unmarshaller, scope types.Scope, item *types.MarshalItem) (interface{}, error)
type Plugin ¶
type PluginGeneratorInterface ¶
type PluginGeneratorInterface = types.PluginGeneratorInterface
type PluginInfo ¶
type PluginInfo = types.PluginInfo
type ReplayUnmarshaller ¶
type ReplayUnmarshaller struct{}
func (ReplayUnmarshaller) Unmarshal ¶
func (self ReplayUnmarshaller) Unmarshal( unmarshaller types.Unmarshaller, scope types.Scope, item *types.MarshalItem) (interface{}, error)
type RowEncoder ¶
type ScopeUnmarshaller ¶
type ScopeUnmarshaller = scope.ScopeUnmarshaller
type StoredExpression ¶
type StoredExpression struct { Expr *_AndExpression // contains filtered or unexported fields }
Unlike the LazyExpr the value of StoredExpression is not cached - this means each time it is evaluated, the expression is fully expanded. NOTE: The StoredExpression is evaluated at the point of reference not at the point of definition - therefore when evaluated, we must provide the scope at that point.
func (*StoredExpression) Call ¶
func (self *StoredExpression) Call(ctx context.Context, scope types.Scope, args *ordereddict.Dict) types.Any
Act as a function
func (*StoredExpression) Marshal ¶
func (self *StoredExpression) Marshal( scope types.Scope) (*types.MarshalItem, error)
type StoredQuery ¶
type StoredQuery = types.StoredQuery
type StoredQueryCallSite ¶
type StoredQueryCallSite struct {
// contains filtered or unexported fields
}
A wrapper around a stored query which captures its call site's parameters in a new scope. When the wrapper is evaluated, the call site's scope will be used.
type StoredQueryItem ¶
type TimeThrottler ¶
type TimeThrottler struct {
// contains filtered or unexported fields
}
func (*TimeThrottler) ChargeOp ¶
func (self *TimeThrottler) ChargeOp()
func (*TimeThrottler) Close ¶
func (self *TimeThrottler) Close()
type VFilterJsonResult ¶
A response from VQL queries.
type VQL ¶
type VQL struct { Let string `LET @Ident ` Parameters *_ParameterList `{ "(" @@ ")" }` LetOperator string ` ( @"=" | @"<=" ) ` StoredQuery *_Select ` ( @@ | ` Expression *_AndExpression ` @@ ) |` Query *_Select ` @@ ` Comments []*_Comment }
An opaque object representing the VQL expression.
func MultiParse ¶
Parse a string into multiple VQL statements.
func MultiParseWithComments ¶
Parse a string into multiple VQL statements.
type Visitor ¶
type Visitor struct { Fragments []string // contains filtered or unexported fields }
func NewVisitor ¶
func NewVisitor(scope types.Scope, options FormatOptions) *Visitor
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
_examples
|
|
Utility functions for extracting and validating inputs to functions and plugins.
|
Utility functions for extracting and validating inputs to functions and plugins. |