From a49a91c8b6b8ed188108e243abf05580afd8ebb3 Mon Sep 17 00:00:00 2001 From: Viking <74813175+xuyukeviki@users.noreply.github.com> Date: Mon, 10 Jul 2023 11:34:37 +0800 Subject: [PATCH] feat: implement string function of expr (#2032) Signed-off-by: Viking <1162777073@qq.com> --- pkg/ast/expr.go | 208 +++++++++++++++++++++++++++++++++++++ pkg/ast/expr_ref.go | 18 ++++ pkg/ast/expr_test.go | 237 +++++++++++++++++++++++++++++++++++++++++++ pkg/ast/statement.go | 57 +++++++++++ 4 files changed, 520 insertions(+) create mode 100644 pkg/ast/expr_test.go diff --git a/pkg/ast/expr.go b/pkg/ast/expr.go index 9db02c5fe5..204466f070 100644 --- a/pkg/ast/expr.go +++ b/pkg/ast/expr.go @@ -14,6 +14,11 @@ package ast +import ( + "fmt" + "strconv" +) + type Node interface { node() } @@ -26,6 +31,8 @@ type NameNode interface { type Expr interface { Node expr() + // String function for the explain grammar, convert Expr to String + String() string } type Literal interface { @@ -82,41 +89,101 @@ type Wildcard struct { func (pe *ParenExpr) expr() {} func (pe *ParenExpr) node() {} +func (pe *ParenExpr) String() string { + e := "" + if pe.Expr != nil { + e += pe.Expr.String() + } + return "parenExpr:{ " + e + " }" +} func (ae *ArrowExpr) expr() {} func (ae *ArrowExpr) node() {} +func (ae *ArrowExpr) String() string { + e := "" + if ae.Expr != nil { + e += ae.Expr.String() + } + return "arrowExpr:{ " + e + " }" +} func (be *BracketExpr) expr() {} func (be *BracketExpr) node() {} +func (be *BracketExpr) String() string { + e := "" + if be.Expr != nil { + e += be.Expr.String() + } + return "bracketExpr:{ " + e + " }" +} func (be *ColonExpr) expr() {} func (be *ColonExpr) node() {} +func (be *ColonExpr) String() string { + s := "" + e := "" + if be.Start != nil { + s += "start:{ " + be.Start.String() + " }" + } + if be.End != nil { + if be.Start != nil { + e += ", " + } + e += "end:{ " + be.End.String() + " }" + } + return "ColonExpr:{ " + s + e + " }" +} func (be *IndexExpr) expr() {} func (be *IndexExpr) node() {} +func (be *IndexExpr) String() string { + i := "" + if be.Index != nil { + i += be.Index.String() + } + return i +} func (w *Wildcard) expr() {} func (w *Wildcard) node() {} +func (w *Wildcard) String() string { + return Tokens[w.Token] +} func (bl *BooleanLiteral) expr() {} func (bl *BooleanLiteral) literal() {} func (bl *BooleanLiteral) node() {} +func (bl *BooleanLiteral) String() string { + return strconv.FormatBool(bl.Val) +} func (tl *TimeLiteral) expr() {} func (tl *TimeLiteral) literal() {} func (tl *TimeLiteral) node() {} +func (tl *TimeLiteral) String() string { + return Tokens[tl.Val] +} func (il *IntegerLiteral) expr() {} func (il *IntegerLiteral) literal() {} func (il *IntegerLiteral) node() {} +func (il *IntegerLiteral) String() string { + return strconv.Itoa(il.Val) +} func (nl *NumberLiteral) expr() {} func (nl *NumberLiteral) literal() {} func (nl *NumberLiteral) node() {} +func (nl *NumberLiteral) String() string { + return fmt.Sprintf("%f", nl.Val) +} func (sl *StringLiteral) expr() {} func (sl *StringLiteral) literal() {} func (sl *StringLiteral) node() {} +func (sl *StringLiteral) String() string { + return sl.Val +} type FuncType int @@ -145,6 +212,24 @@ type Call struct { func (c *Call) expr() {} func (c *Call) literal() {} func (c *Call) node() {} +func (c *Call) String() string { + args := "" + if c.Args != nil { + args = ", args:[" + for i, arg := range c.Args { + args += arg.String() + if i != len(c.Args)-1 { + args += ", " + } + } + args += "]" + } + when := "" + if c.WhenExpr != nil { + when += ", when:{ " + c.WhenExpr.String() + " }" + } + return "Call:{ name:" + c.Name + args + when + " }" +} type PartitionExpr struct { Exprs []Expr @@ -152,6 +237,16 @@ type PartitionExpr struct { func (pe *PartitionExpr) expr() {} func (pe *PartitionExpr) node() {} +func (pe *PartitionExpr) String() string { + e := "" + for i, expr := range pe.Exprs { + e += expr.String() + if i != len(pe.Exprs)-1 { + e += ", " + } + } + return "PartitionExpr:[ " + e + " ]" +} type BinaryExpr struct { OP Token @@ -161,6 +256,18 @@ type BinaryExpr struct { func (be *BinaryExpr) expr() {} func (be *BinaryExpr) node() {} +func (be *BinaryExpr) String() string { + info := "" + if be.LHS != nil && be.RHS != nil { + t := Tokens[be.OP] + if t == "[]" { + info += "binaryExpr:{ " + be.LHS.String() + "[" + be.RHS.String() + "] }" + return info + } + info += "binaryExpr:{ " + be.LHS.String() + " " + t + " " + be.RHS.String() + " }" + } + return info +} type WhenClause struct { // The condition Expression @@ -170,6 +277,13 @@ type WhenClause struct { func (w *WhenClause) expr() {} func (w *WhenClause) node() {} +func (w *WhenClause) String() string { + e := "" + if w.Expr != nil { + e += w.Expr.String() + } + return "whenClause:{ " + e + " }" +} type CaseExpr struct { // The compare value Expression. It can be a value Expression or nil. @@ -181,6 +295,29 @@ type CaseExpr struct { func (c *CaseExpr) expr() {} func (c *CaseExpr) node() {} +func (c *CaseExpr) String() string { + v := "" + if c.Value != nil { + v += "value:{ " + c.Value.String() + " }" + } + w := "" + if c.WhenClauses != nil && len(c.WhenClauses) != 0 { + if c.Value != nil { + w += ", " + } + w += "whenClauses:[" + for i, clause := range c.WhenClauses { + if clause.Expr != nil { + w += "{ " + clause.String() + " }" + if i != len(c.WhenClauses)-1 { + w += ", " + } + } + } + w += "]" + } + return "caseExprValue:{ " + v + w + " }" +} type ValueSetExpr struct { LiteralExprs []Expr // ("A", "B", "C") or (1, 2, 3) @@ -189,6 +326,27 @@ type ValueSetExpr struct { func (c *ValueSetExpr) expr() {} func (c *ValueSetExpr) node() {} +func (c *ValueSetExpr) String() string { + le := "" + if c.LiteralExprs != nil && len(c.LiteralExprs) != 0 { + le += "literalExprs:[" + for i, expr := range c.LiteralExprs { + le += expr.String() + if i != len(c.LiteralExprs)-1 { + le += ", " + } + } + le += "]" + } + a := "" + if c.ArrayExpr != nil { + if c.LiteralExprs != nil && len(c.LiteralExprs) != 0 { + a += ", " + } + a += "arrayExpr:{ " + c.ArrayExpr.String() + " }" + } + return "valueSetExpr:{ " + le + a + " }" +} type BetweenExpr struct { Lower Expr @@ -197,6 +355,20 @@ type BetweenExpr struct { func (b *BetweenExpr) expr() {} func (b *BetweenExpr) node() {} +func (b *BetweenExpr) String() string { + low := "" + high := "" + if b.Lower != nil { + low += b.Lower.String() + } + if b.Higher != nil { + if b.Lower != nil { + high += ", " + } + high += b.Higher.String() + } + return "betweenExpr:{ " + low + high + " }" +} type LimitExpr struct { LimitCount *IntegerLiteral @@ -204,6 +376,12 @@ type LimitExpr struct { func (l *LimitExpr) expr() {} func (l *LimitExpr) node() {} +func (l *LimitExpr) String() string { + if l.LimitCount != nil { + return "limitExpr:{ " + l.LimitCount.String() + " }" + } + return "" +} type StreamName string @@ -221,6 +399,20 @@ type MetaRef struct { func (fr *MetaRef) expr() {} func (fr *MetaRef) node() {} +func (fr *MetaRef) String() string { + sn := "" + n := "" + if fr.StreamName != "" { + sn += "streamName:" + string(fr.StreamName) + } + if fr.Name != "" { + if fr.StreamName != "" { + n += ", " + } + n += "fieldName:" + fr.Name + } + return "metaRef:{ " + sn + n + " }" +} type JsonFieldRef struct { Name string @@ -228,6 +420,9 @@ type JsonFieldRef struct { func (fr *JsonFieldRef) expr() {} func (fr *JsonFieldRef) node() {} +func (fr *JsonFieldRef) String() string { + return "jsonFieldName:" + fr.Name +} type ColFuncField struct { Name string @@ -236,3 +431,16 @@ type ColFuncField struct { func (fr *ColFuncField) expr() {} func (fr *ColFuncField) node() {} +func (fr *ColFuncField) String() string { + e := "" + if fr.Name != "" { + e += "name: " + fr.Name + } + if fr.Expr != nil { + if fr.Name != "" { + e += ", " + } + e += "expr:{ " + fr.Expr.String() + " }" + } + return "colFuncField:{ " + e + " }" +} diff --git a/pkg/ast/expr_ref.go b/pkg/ast/expr_ref.go index 522a234942..5d3592a38c 100644 --- a/pkg/ast/expr_ref.go +++ b/pkg/ast/expr_ref.go @@ -27,6 +27,9 @@ type LikePattern struct { func (l *LikePattern) expr() {} func (l *LikePattern) node() {} +func (l *LikePattern) String() string { + return "likePattern:" + l.Pattern.String() +} func (l *LikePattern) Compile(likestr string) (*regexp.Regexp, error) { regstr := strings.ReplaceAll(strings.NewReplacer( @@ -64,6 +67,21 @@ func (fr *FieldRef) IsColumn() bool { return fr.StreamName != AliasStream && fr.StreamName != "" } +func (fr *FieldRef) String() string { + sn := "" + n := "" + if fr.StreamName != "" { + sn += string(fr.StreamName) + } + if fr.Name != "" { + if fr.StreamName != "" { + n += "." + } + n += fr.Name + } + return sn + n +} + func (fr *FieldRef) IsAlias() bool { return fr.StreamName == AliasStream } diff --git a/pkg/ast/expr_test.go b/pkg/ast/expr_test.go new file mode 100644 index 0000000000..ee8c6911d1 --- /dev/null +++ b/pkg/ast/expr_test.go @@ -0,0 +1,237 @@ +package ast + +import ( + "math" + "regexp" + "testing" + + "github.com/lf-edge/ekuiper/internal/testx" +) + +func init() { + testx.InitEnv() +} + +func Test_exprStringPlan(t *testing.T) { + re1, _ := regexp.Compile("^foo$") + test := []struct { + e Expr + res string + }{ + { + e: &BetweenExpr{ + Lower: &IntegerLiteral{ + Val: 0, + }, + Higher: &IntegerLiteral{ + Val: 10, + }, + }, + res: "betweenExpr:{ 0, 10 }", + }, + { + e: &BinaryExpr{ + OP: SUBSET, + LHS: &FieldRef{ + StreamName: "src1", + Name: "myarray", + }, + RHS: &IndexExpr{Index: &FieldRef{ + StreamName: "src1", + Name: "temp", + }}, + }, + res: "binaryExpr:{ src1.myarray[src1.temp] }", + }, + { + e: &BooleanLiteral{Val: true}, + res: "true", + }, + { + e: &Call{Name: "count", FuncId: 0, Args: []Expr{&Wildcard{ + Token: ASTERISK, + }}, FuncType: FuncTypeAgg}, + res: "Call:{ name:count, args:[*] }", + }, + { + e: &CaseExpr{ + WhenClauses: []*WhenClause{ + { + Expr: &BinaryExpr{ + OP: BETWEEN, + LHS: &Call{ + Name: "lag", + FuncId: 0, + FuncType: FuncType(0), + Args: []Expr{ + &FieldRef{ + StreamName: "src1", + Name: "temp", + }, + }, + CachedField: "$$a_lag_0", + Cached: true, + WhenExpr: &BinaryExpr{ + OP: GT, + LHS: &Call{ + Name: "lag", + FuncId: 1, + FuncType: FuncType(0), + Args: []Expr{ + &FieldRef{ + StreamName: "src1", + Name: "id1", + }, + }, + CachedField: "$$a_lag_1", + Cached: true, + }, + RHS: &IntegerLiteral{ + Val: 1, + }, + }, + }, + RHS: &BetweenExpr{ + Lower: &IntegerLiteral{ + Val: 0, + }, + Higher: &IntegerLiteral{ + Val: 10, + }, + }, + }, + Result: &IntegerLiteral{ + Val: 1, + }, + }, + { + &BinaryExpr{ + OP: BETWEEN, + LHS: &Call{ + Name: "lag", + FuncId: 0, + FuncType: FuncType(0), + Args: []Expr{ + &FieldRef{ + StreamName: "src1", + Name: "temp", + }, + }, + CachedField: "$$a_lag_0", + Cached: true, + WhenExpr: &BinaryExpr{ + OP: GT, + LHS: &Call{ + Name: "lag", + FuncId: 1, + FuncType: FuncType(0), + Args: []Expr{ + &FieldRef{ + StreamName: "src1", + Name: "id1", + }, + }, + CachedField: "$$a_lag_1", + Cached: true, + }, + RHS: &IntegerLiteral{ + Val: 1, + }, + }, + }, + RHS: &BetweenExpr{ + Lower: &IntegerLiteral{ + Val: 0, + }, + Higher: &IntegerLiteral{ + Val: 10, + }, + }, + }, + &IntegerLiteral{ + Val: 2, + }, + }, + }, + ElseClause: &IntegerLiteral{ + Val: 0, + }, + Value: &IntegerLiteral{ + Val: 12, + }, + }, + res: "caseExprValue:{ value:{ 12 }, whenClauses:[{ whenClause:{ binaryExpr:{ Call:{ name:lag, args:[src1.temp], when:{ binaryExpr:{ Call:{ name:lag, args:[src1.id1] } > 1 } } } BETWEEN betweenExpr:{ 0, 10 } } } }, { whenClause:{ binaryExpr:{ Call:{ name:lag, args:[src1.temp], when:{ binaryExpr:{ Call:{ name:lag, args:[src1.id1] } > 1 } } } BETWEEN betweenExpr:{ 0, 10 } } } }] }", + }, + { + e: &JsonFieldRef{Name: "Device"}, + res: "jsonFieldName:Device", + }, + { + e: &NumberLiteral{Val: 1.23}, + res: "1.230000", + }, + { + e: &StringLiteral{Val: "v1"}, + res: "v1", + }, + { + e: &TimeLiteral{Val: 2}, + res: "WS", + }, + { + e: &MetaRef{ + Name: "device", + StreamName: DefaultStream, + }, + res: "metaRef:{ streamName:$$default, fieldName:device }", + }, + { + e: &PartitionExpr{Exprs: []Expr{&FieldRef{Name: "temp", StreamName: "src1"}, &FieldRef{Name: "current", StreamName: "src2"}}}, + res: "PartitionExpr:[ src1.temp, src2.current ]", + }, + { + e: &SortField{Uname: "name", Name: "name", Ascending: true, FieldExpr: &FieldRef{Name: "name", StreamName: DefaultStream}}, + res: "sortField:{ name:name, ascending:true, fieldExpr:{ $$default.name } }", + }, + { + e: &BracketExpr{Expr: &ColonExpr{Start: &IntegerLiteral{Val: 0}, End: &IntegerLiteral{Val: math.MinInt32}}}, + res: "bracketExpr:{ ColonExpr:{ start:{ 0 }, end:{ -2147483648 } } }", + }, + { + e: &ArrowExpr{Expr: &ColonExpr{Start: &IntegerLiteral{Val: 0}, End: &IntegerLiteral{Val: math.MinInt32}}}, + res: "arrowExpr:{ ColonExpr:{ start:{ 0 }, end:{ -2147483648 } } }", + }, + { + e: &ValueSetExpr{ + LiteralExprs: []Expr{&StringLiteral{"A"}, &StringLiteral{"B"}}, + ArrayExpr: &StringLiteral{"A, B"}, + }, + res: "valueSetExpr:{ literalExprs:[A, B], arrayExpr:{ A, B } }", + }, + { + e: &ColFuncField{ + Name: "ABC", + Expr: &StringLiteral{Val: ""}, + }, + res: "colFuncField:{ name: ABC, expr:{ } }", + }, + { + e: &LikePattern{Expr: &StringLiteral{Val: "foo"}, Pattern: re1}, + res: "likePattern:^foo$", + }, + { + e: &LimitExpr{ + LimitCount: &IntegerLiteral{Val: 10}, + }, + res: "limitExpr:{ 10 }", + }, + } + + for i := 0; i < len(test); i++ { + res := test[i].res + str := test[i].e.String() + if str != res { + t.Errorf("case %d: expect validate %v but got %v", i, res, str) + } + } +} diff --git a/pkg/ast/statement.go b/pkg/ast/statement.go index 7e96b5b873..d764ee5df1 100644 --- a/pkg/ast/statement.go +++ b/pkg/ast/statement.go @@ -14,6 +14,8 @@ package ast +import "strconv" + type Statement interface { stmt() Node @@ -119,6 +121,23 @@ const ( CROSS_JOIN ) +func (j JoinType) String() string { + switch j { + case LEFT_JOIN: + return "LEFT_JOIN" + case INNER_JOIN: + return "INNER_JOIN" + case RIGHT_JOIN: + return "RIGHT_JOIN" + case FULL_JOIN: + return "FULL_JOIN" + case CROSS_JOIN: + return "CROSS_JOIN" + default: + return "" + } +} + type Join struct { Name string Alias string @@ -172,6 +191,24 @@ const ( COUNT_WINDOW ) +func (w WindowType) String() string { + switch w { + case NOT_WINDOW: + return "NOT_WINDOW" + case TUMBLING_WINDOW: + return "TUMBLING_WINDOW" + case HOPPING_WINDOW: + return "HOPPING_WINDOW" + case SLIDING_WINDOW: + return "SLIDING_WINDOW" + case SESSION_WINDOW: + return "SESSION_WINDOW" + case COUNT_WINDOW: + return "COUNT_WINDOW" + } + return "" +} + type Window struct { TriggerCondition Expr WindowType WindowType @@ -193,6 +230,26 @@ type SortField struct { Expr } +func (sf *SortField) String() string { + fe := "" + if sf.FieldExpr != nil { + fe += ", fieldExpr:{ " + sf.FieldExpr.String() + " }" + } + return "sortField:{ name:" + sf.Name + ", ascending:" + strconv.FormatBool(sf.Ascending) + fe + " }" +} + +func (wd *Window) String() string { + tu := "" + if wd.TimeUnit != nil { + tu += ", timeUnit: " + wd.TimeUnit.String() + " " + } + filter := "" + if wd.Filter != nil { + filter += ", " + wd.Filter.String() + } + return "window:{ windowType:" + wd.WindowType.String() + tu + filter + " }" +} + type SortFields []SortField func (d SortFields) node() {}