diff options
| author | Paul Buetow <paul@buetow.org> | 2023-09-05 16:38:23 +0300 |
|---|---|---|
| committer | Paul Buetow <pbuetow@mimecast.com> | 2023-09-07 15:32:29 +0300 |
| commit | 9c77304550d65b8e7c2b724b991eef0dbc13694a (patch) | |
| tree | b0401269acf383760e2b2f962e71d11fd55147d2 /internal/mapr | |
| parent | 360f67bf536372cb6a78fe35c15ba6128fda290b (diff) | |
Can quote fields in select conditions, e.g. select `count($foo)`, ..
Diffstat (limited to 'internal/mapr')
| -rw-r--r-- | internal/mapr/query.go | 10 | ||||
| -rw-r--r-- | internal/mapr/query_test.go | 40 | ||||
| -rw-r--r-- | internal/mapr/selectcondition.go | 19 | ||||
| -rw-r--r-- | internal/mapr/token.go | 10 |
4 files changed, 64 insertions, 15 deletions
diff --git a/internal/mapr/query.go b/internal/mapr/query.go index 247cdaf..ddcbc90 100644 --- a/internal/mapr/query.go +++ b/internal/mapr/query.go @@ -73,6 +73,13 @@ func NewQuery(queryStr string) (*Query, error) { Interval: time.Second * 5, Limit: -1, } + + // If log format is CSV, then use "." as the table. It means, that + // we don't do any file filtering, we process all lines of the CSV. + if q.LogFormat == "csv" { + q.Table = "." + } + return &q, q.parse(tokens) } @@ -87,8 +94,7 @@ func (q *Query) Has(what string) bool { } func (q *Query) parse(tokens []token) error { - tokens, err := q.parseTokens(tokens) - if err != nil { + if _, err := q.parseTokens(tokens); err != nil { return err } diff --git a/internal/mapr/query_test.go b/internal/mapr/query_test.go index f03ccba..f37b8d4 100644 --- a/internal/mapr/query_test.go +++ b/internal/mapr/query_test.go @@ -252,3 +252,43 @@ func TestParseQueryDeep(t *testing.T) { } } } + +func TestQuotedSelectCondition(t *testing.T) { + queryStr := "select `count($foo)`, foo, $foo, count($foo) logformat csv" + + q, err := NewQuery(queryStr) + if err != nil { + t.Errorf("Query parse error: %s\n%v: %v", queryStr, q, err) + } + if len(q.Select) != 4 { + t.Errorf("Expected three elements in 'select' clause but got '%v': %s\n%v", + q.Select, queryStr, q) + } + + if q.Select[0].Field != "count($foo)" { + t.Errorf("Expected 'num($foo)' as first element in 'select' clause but got '%v': %s\n%v", + q.Select[0].Field, queryStr, q) + } + if q.Select[0].Operation != Last { + t.Errorf("Expected 'Last' as aggregation function of first element in "+ + "'select' clause but got '%v': %s\n%v", q.Select[0].Operation, queryStr, q) + } + + if q.Select[1].Field != "foo" { + t.Errorf("Expected 'foo' as first element in 'select' clause but got '%v': %s\n%v", + q.Select[1].Field, queryStr, q) + } + if q.Select[2].Field != "$foo" { + t.Errorf("Expected '$foo' as first element in 'select' clause but got '%v': %s\n%v", + q.Select[2].Field, queryStr, q) + } + + if q.Select[3].Field != "$foo" { + t.Errorf("Expected '$foo' as first element in 'select' clause but got '%v': %s\n%v", + q.Select[3].Field, queryStr, q) + } + if q.Select[3].Operation != Count { + t.Errorf("Expected 'count' as aggregation function of thourth element in "+ + "'select' clause but got '%v': %s\n%v", q.Select[3].Operation, queryStr, q) + } +} diff --git a/internal/mapr/selectcondition.go b/internal/mapr/selectcondition.go index 45fc16b..78359c7 100644 --- a/internal/mapr/selectcondition.go +++ b/internal/mapr/selectcondition.go @@ -40,16 +40,18 @@ func makeSelectConditions(tokens []token) ([]selectCondition, error) { // Parse select aggregation, e.g. sum(foo) parse := func(token token) (selectCondition, error) { var sc selectCondition - tokenStr := token.str - if !strings.Contains(tokenStr, "(") && !strings.Contains(tokenStr, ")") { - sc.Field = tokenStr - sc.FieldStorage = tokenStr + // With quotes stripped: We got a quoted select expression, e.g. + // "select `count($foo)` ...", which will literaly look for field + // "count($foo)" without performing the count aggregation. + if token.quotesStripped || (!strings.Contains(token.str, "(") && !strings.Contains(token.str, ")")) { + sc.Field = token.str + sc.FieldStorage = token.str sc.Operation = Last return sc, nil } - a := strings.Split(tokenStr, "(") + a := strings.Split(token.str, "(") if len(a) != 2 { return sc, errors.New(invalidQuery + "Can't parse 'select' aggregation: " + token.str) @@ -61,8 +63,8 @@ func makeSelectConditions(tokens []token) ([]selectCondition, error) { return sc, errors.New(invalidQuery + "Can't parse 'select' field name " + "from aggregation: " + token.str) } - sc.Field = b[0] // Field name, e.g. 'foo' - sc.FieldStorage = tokenStr // e.g. 'sum(foo)' + sc.Field = b[0] // Field name, e.g. 'foo' + sc.FieldStorage = token.str // e.g. 'sum(foo)' switch agg { case "count": @@ -80,8 +82,7 @@ func makeSelectConditions(tokens []token) ([]selectCondition, error) { case "len": sc.Operation = Len default: - return sc, errors.New(invalidQuery + - "Unknown aggregation in 'select' clause: " + agg) + return sc, errors.New(invalidQuery + "Unknown aggregation in 'select' clause: " + agg) } return sc, nil } diff --git a/internal/mapr/token.go b/internal/mapr/token.go index 6ac7631..48d1192 100644 --- a/internal/mapr/token.go +++ b/internal/mapr/token.go @@ -9,8 +9,9 @@ var keywords = [...]string{"select", "from", "where", "set", "group", "rorder", // Represents a parsed token, used to parse the mapr query. type token struct { - str string - isBareword bool + str string + isBareword bool + quotesStripped bool } func (t token) isKeyword() bool { @@ -71,8 +72,9 @@ func tokensConsume(tokens []token) ([]token, []token) { stripped := t.str[1 : length-1] //dlog.Common.Trace("stripped", stripped) t := token{ - str: stripped, - isBareword: t.isBareword, + str: stripped, + isBareword: t.isBareword, + quotesStripped: true, } consumed = append(consumed, t) continue |
