From a100305fe7a04ea1bf9ecfd562edb8f4d4f3277c Mon Sep 17 00:00:00 2001 From: Mike Cohen Date: Tue, 8 Oct 2024 18:46:37 +1000 Subject: [PATCH] Bugfix: Apply LIMIT to output of GROUP BY Also added && shortcut operator (Similar to the || operator) --- fixtures/vql_queries.golden | 12 +++++++++++- vfilter.go | 33 +++++++++++++++++++++++---------- vfilter_test.go | 3 +++ 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/fixtures/vql_queries.golden b/fixtures/vql_queries.golden index dc74a95..325034d 100644 --- a/fixtures/vql_queries.golden +++ b/fixtures/vql_queries.golden @@ -665,7 +665,17 @@ "'A' || '' || 'B'": "A" } ], - "076 Whitespace in the query: SELECT * FROM test()": [ + "076 Alternatives with AND shortcut operator: SELECT NULL \u0026\u0026 '', TRUE \u0026\u0026 'XX', 'A' \u0026\u0026 'B', 'A' \u0026\u0026 FALSE, ((FALSE \u0026\u0026 1) || 2), TRUE \u0026\u0026 1 || 2 FROM scope()": [ + { + "NULL \u0026\u0026 ''": false, + "TRUE \u0026\u0026 'XX'": "XX", + "'A' \u0026\u0026 'B'": "B", + "'A' \u0026\u0026 FALSE": false, + "((FALSE \u0026\u0026 1) || 2)": 2, + "TRUE \u0026\u0026 1 || 2": 1 + } + ], + "077 Whitespace in the query: SELECT * FROM test()": [ { "foo": 0, "bar": 0 diff --git a/vfilter.go b/vfilter.go index 68a8e8f..5ddd2a4 100644 --- a/vfilter.go +++ b/vfilter.go @@ -156,6 +156,7 @@ var ( `|(?ims)(?P\bAND\b)` + `|(?ims)(?P\bOR\b)` + `|(?ims)(?P\|+)` + + `|(?ims)(?P&&)` + `|(?ims)(?P\bFROM\b)` + `|(?ims)(?P\bNOT\b)` + `|(?ims)(?P\bAS\b)` + @@ -475,12 +476,10 @@ func (self *_Select) Eval(ctx context.Context, scope types.Scope) <-chan Row { // Start query evaluation scope.Explainer().StartQuery(self) - if self.GroupBy != nil { - return self.EvalGroupBy(ctx, scope) - } - output_chan := make(chan Row) + // Limits occur before the group by so we can cut the group by + // result short according to the limit clause. if self.Limit != nil { go func() { defer close(output_chan) @@ -511,6 +510,12 @@ func (self *_Select) Eval(ctx context.Context, scope types.Scope) <-chan Row { return output_chan } + // Group by occurs before order by so we can order the grouped by + // results. + if self.GroupBy != nil { + return self.EvalGroupBy(ctx, scope) + } + if self.OrderBy != nil { desc := false if self.OrderByDesc != nil { @@ -813,7 +818,7 @@ type _AndExpression struct { } type _OpAndTerm struct { - Operator string ` @AND ` + Operator string ` ( @AND | @AlternativeAND ) ` Term *_OrExpression `@@` } @@ -1268,22 +1273,30 @@ func (self *_AndExpression) IsAggregate(scope types.Scope) bool { } func (self *_AndExpression) Reduce(ctx context.Context, scope types.Scope) Any { - result := self.Left.Reduce(ctx, scope) + left := self.Left.Reduce(ctx, scope) if self.Right == nil { - return result + return left } - if scope.Bool(result) == false { + if scope.Bool(left) == false { return false } + last := left for _, term := range self.Right { - if scope.Bool(term.Term.Reduce(ctx, scope)) == false { + right := term.Term.Reduce(ctx, scope) + if scope.Bool(right) == false { return false } + + if term.Operator == "&&" { + last = right + } else { + last = true + } } - return true + return last } func (self *_OrExpression) IsAggregate(scope types.Scope) bool { diff --git a/vfilter_test.go b/vfilter_test.go index 94fe789..c2978db 100644 --- a/vfilter_test.go +++ b/vfilter_test.go @@ -732,6 +732,9 @@ b={ {"Alternatives with the OR shortcut operator false", "SELECT NULL || '', NULL || FALSE, NULL || 'X', 'A' || 'B', 'A' || FALSE, 'A' || '' || 'B' FROM scope()"}, + {"Alternatives with AND shortcut operator", + "SELECT NULL && '', TRUE && 'XX', 'A' && 'B', 'A' && FALSE, ((FALSE && 1) || 2), TRUE && 1 || 2 FROM scope()"}, + {"Whitespace in the query", "SELECT * FROM\ntest()"}, }