diff --git a/predicate/logical.go b/predicate/logical.go index 1ddda57209..37048705f8 100644 --- a/predicate/logical.go +++ b/predicate/logical.go @@ -31,7 +31,7 @@ func (op LogicalOperator) Value() (datatypes.Node_Logical, error) { // LogicalNode is a node type includes a logical expression with other nodes. type LogicalNode struct { Operator LogicalOperator `json:"operator"` - Children []Node `json:"children"` + Children [2]Node `json:"children"` } // ToDataType convert a LogicalNode to datatypes.Node. diff --git a/predicate/parser.go b/predicate/parser.go index 83a3b6b08a..22eb0bff6f 100644 --- a/predicate/parser.go +++ b/predicate/parser.go @@ -73,9 +73,7 @@ func Parse(sts string) (n Node, err error) { } func (p *parser) parseLogicalNode() (Node, error) { - n := &LogicalNode{ - Children: make([]Node, 0), - } + n := new(LogicalNode) for { tok, pos, _ := p.scanIgnoreWhitespace() switch tok { @@ -91,9 +89,30 @@ func (p *parser) parseLogicalNode() (Node, error) { if err != nil { return *n, err } - n.Children = append(n.Children, tr) + if n.Children[0] == nil { + n.Children[0] = tr + } else { + n.Children[1] = tr + } case influxql.AND: n.Operator = LogicalAnd + if n.Children[1] == nil { + continue + } + var n1 Node + var err error + if tokNext := p.peekTok(); tokNext == influxql.LPAREN { + n1, err = p.parseLogicalNode() + } else { + n1, err = p.parseTagRuleNode() + } + if err != nil { + return *n, err + } + n = &LogicalNode{ + Children: [2]Node{*n, n1}, + Operator: LogicalAnd, + } case influxql.OR: return *n, &influxdb.Error{ Code: influxdb.EInvalid, @@ -112,7 +131,11 @@ func (p *parser) parseLogicalNode() (Node, error) { Msg: fmt.Sprintf("extra ( seen"), } } - n.Children = append(n.Children, n1) + if n.Children[0] == nil { + n.Children[0] = n1 + } else { + n.Children[1] = n1 + } case influxql.RPAREN: p.openParen-- fallthrough @@ -123,7 +146,7 @@ func (p *parser) parseLogicalNode() (Node, error) { Msg: fmt.Sprintf("extra ) seen"), } } - if len(n.Children) == 1 { + if n.Children[1] == nil { return n.Children[0], nil } return *n, nil @@ -201,3 +224,13 @@ scanRegularTagValue: } } } + +// peekRune returns the next rune that would be read by the scanner. +func (p *parser) peekTok() influxql.Token { + tok, _, _ := p.scanIgnoreWhitespace() + if tok != influxql.EOF { + p.unscan() + } + + return tok +} diff --git a/predicate/parser_test.go b/predicate/parser_test.go index f216fbd4f9..26a1803bed 100644 --- a/predicate/parser_test.go +++ b/predicate/parser_test.go @@ -18,10 +18,23 @@ func TestParseNode(t *testing.T) { err error }{ { - str: ` abc="opq" AND gender="male" AND temp=1123`, - node: LogicalNode{Operator: LogicalAnd, Children: []Node{ + str: `abc=opq`, + node: TagRuleNode{Tag: influxdb.Tag{Key: "abc", Value: "opq"}}, + }, + { + str: `abc=opq and gender="male"`, + node: LogicalNode{Operator: LogicalAnd, Children: [2]Node{ TagRuleNode{Tag: influxdb.Tag{Key: "abc", Value: "opq"}}, TagRuleNode{Tag: influxdb.Tag{Key: "gender", Value: "male"}}, + }}, + }, + { + str: ` abc="opq" AND gender="male" AND temp=1123`, + node: LogicalNode{Operator: LogicalAnd, Children: [2]Node{ + LogicalNode{Operator: LogicalAnd, Children: [2]Node{ + TagRuleNode{Tag: influxdb.Tag{Key: "abc", Value: "opq"}}, + TagRuleNode{Tag: influxdb.Tag{Key: "gender", Value: "male"}}, + }}, TagRuleNode{Tag: influxdb.Tag{Key: "temp", Value: "1123"}}, }}, }, @@ -34,16 +47,18 @@ func TestParseNode(t *testing.T) { }, { str: ` (t1="v1" and t2="v2") and (t3=v3 and (t4=v4 and t5=v5 and t6=v6))`, - node: LogicalNode{Operator: LogicalAnd, Children: []Node{ - LogicalNode{Operator: LogicalAnd, Children: []Node{ + node: LogicalNode{Operator: LogicalAnd, Children: [2]Node{ + LogicalNode{Operator: LogicalAnd, Children: [2]Node{ TagRuleNode{Tag: influxdb.Tag{Key: "t1", Value: "v1"}}, TagRuleNode{Tag: influxdb.Tag{Key: "t2", Value: "v2"}}, }}, - LogicalNode{Operator: LogicalAnd, Children: []Node{ + LogicalNode{Operator: LogicalAnd, Children: [2]Node{ TagRuleNode{Tag: influxdb.Tag{Key: "t3", Value: "v3"}}, - LogicalNode{Operator: LogicalAnd, Children: []Node{ - TagRuleNode{Tag: influxdb.Tag{Key: "t4", Value: "v4"}}, - TagRuleNode{Tag: influxdb.Tag{Key: "t5", Value: "v5"}}, + LogicalNode{Operator: LogicalAnd, Children: [2]Node{ + LogicalNode{Operator: LogicalAnd, Children: [2]Node{ + TagRuleNode{Tag: influxdb.Tag{Key: "t4", Value: "v4"}}, + TagRuleNode{Tag: influxdb.Tag{Key: "t5", Value: "v5"}}, + }}, TagRuleNode{Tag: influxdb.Tag{Key: "t6", Value: "v6"}}, }}, }}, diff --git a/predicate/predicate_test.go b/predicate/predicate_test.go index 980e6f3c29..b6d2c5d669 100644 --- a/predicate/predicate_test.go +++ b/predicate/predicate_test.go @@ -5,6 +5,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/influxdata/influxdb" + "github.com/influxdata/influxdb/models" "github.com/influxdata/influxdb/storage/reads/datatypes" influxtesting "github.com/influxdata/influxdb/testing" ) @@ -45,11 +46,63 @@ func TestDataTypeConversion(t *testing.T) { }, }, }, + { + name: "measurement tag rule", + node: &TagRuleNode{ + Operator: influxdb.Equal, + Tag: influxdb.Tag{ + Key: "_measurement", + Value: "cpu", + }, + }, + dataType: &datatypes.Node{ + NodeType: datatypes.NodeTypeComparisonExpression, + Value: &datatypes.Node_Comparison_{Comparison: datatypes.ComparisonEqual}, + Children: []*datatypes.Node{ + { + NodeType: datatypes.NodeTypeTagRef, + Value: &datatypes.Node_TagRefValue{TagRefValue: models.MeasurementTagKey}, + }, + { + NodeType: datatypes.NodeTypeLiteral, + Value: &datatypes.Node_StringValue{ + StringValue: "cpu", + }, + }, + }, + }, + }, + { + name: "field tag rule", + node: &TagRuleNode{ + Operator: influxdb.Equal, + Tag: influxdb.Tag{ + Key: "_field", + Value: "cpu", + }, + }, + dataType: &datatypes.Node{ + NodeType: datatypes.NodeTypeComparisonExpression, + Value: &datatypes.Node_Comparison_{Comparison: datatypes.ComparisonEqual}, + Children: []*datatypes.Node{ + { + NodeType: datatypes.NodeTypeTagRef, + Value: &datatypes.Node_TagRefValue{TagRefValue: models.FieldKeyTagKey}, + }, + { + NodeType: datatypes.NodeTypeLiteral, + Value: &datatypes.Node_StringValue{ + StringValue: "cpu", + }, + }, + }, + }, + }, { name: "logical", node: &LogicalNode{ Operator: LogicalAnd, - Children: []Node{ + Children: [2]Node{ &TagRuleNode{ Operator: influxdb.Equal, Tag: influxdb.Tag{ @@ -111,10 +164,10 @@ func TestDataTypeConversion(t *testing.T) { name: "conplex logical", node: &LogicalNode{ Operator: LogicalAnd, - Children: []Node{ + Children: [2]Node{ &LogicalNode{ Operator: LogicalAnd, - Children: []Node{ + Children: [2]Node{ &TagRuleNode{ Operator: influxdb.Equal, Tag: influxdb.Tag{ diff --git a/predicate/tag_rule.go b/predicate/tag_rule.go index fa519b456f..5a4f72dae4 100644 --- a/predicate/tag_rule.go +++ b/predicate/tag_rule.go @@ -4,12 +4,18 @@ import ( "fmt" "github.com/influxdata/influxdb" + "github.com/influxdata/influxdb/models" "github.com/influxdata/influxdb/storage/reads/datatypes" ) // TagRuleNode is a node type of a single tag rule. type TagRuleNode influxdb.TagRule +var specialKey = map[string]string{ + "_measurement": models.MeasurementTagKey, + "_field": models.FieldKeyTagKey, +} + // NodeTypeLiteral convert a TagRuleNode to a nodeTypeLiteral. func NodeTypeLiteral(tr TagRuleNode) *datatypes.Node { switch tr.Operator { @@ -61,6 +67,9 @@ func (n TagRuleNode) ToDataType() (*datatypes.Node, error) { if err != nil { return nil, err } + if special, ok := specialKey[n.Key]; ok { + n.Key = special + } return &datatypes.Node{ NodeType: datatypes.NodeTypeComparisonExpression, Value: &datatypes.Node_Comparison_{Comparison: compare},