Repository: lf-edge/ekuiper
Pull request #946 (2021-08-17 03:53:25)

fix(sql): distinguish json field ref from field ref
Currently, a->b are parsed as FieldRef which is the same model as a field. This brings some problems such as field alias may conflict with the json field.

Closes: #945


Patch:
diff --git a/internal/topo/planner/analyzer.go b/internal/topo/planner/analyzer.go
index 2e57594c..6d936054 100644
--- a/internal/topo/planner/analyzer.go
+++ b/internal/topo/planner/analyzer.go
@@ -63,9 +63,7 @@ func decorateStmt(s *ast.SelectStatement, store kv.KeyValue) ([]*ast.StreamStmt,
 		ast.WalkFunc(f.Expr, func(n ast.Node) bool {
 			switch f := n.(type) {
 			case *ast.FieldRef:
-				if f.IsSQLField() {
-					walkErr = fieldsMap.bind(f)
-				}
+				walkErr = fieldsMap.bind(f)
 			}
 			return true
 		})
diff --git a/internal/xsql/parser.go b/internal/xsql/parser.go
index 7792006c..97da64dc 100644
--- a/internal/xsql/parser.go
+++ b/internal/xsql/parser.go
@@ -528,7 +528,7 @@ func (p *Parser) parseUnaryExpr(isSubField bool) (ast.Expr, error) {
 					return &ast.MetaRef{StreamName: ast.StreamName(n[0]), Name: n[1]}, nil
 				}
 				if isSubField {
-					return &ast.MetaRef{StreamName: "", Name: n[0]}, nil
+					return &ast.JsonFieldRef{Name: n[0]}, nil
 				}
 				return &ast.MetaRef{StreamName: ast.DefaultStream, Name: n[0]}, nil
 			} else {
@@ -536,7 +536,7 @@ func (p *Parser) parseUnaryExpr(isSubField bool) (ast.Expr, error) {
 					return &ast.FieldRef{StreamName: ast.StreamName(n[0]), Name: n[1]}, nil
 				}
 				if isSubField {
-					return &ast.FieldRef{StreamName: "", Name: n[0]}, nil
+					return &ast.JsonFieldRef{Name: n[0]}, nil
 				}
 				return &ast.FieldRef{StreamName: ast.DefaultStream, Name: n[0]}, nil
 			}
diff --git a/internal/xsql/valuer.go b/internal/xsql/valuer.go
index 822aaf55..180d1404 100644
--- a/internal/xsql/valuer.go
+++ b/internal/xsql/valuer.go
@@ -432,6 +432,13 @@ func (v *ValuerEval) Eval(expr ast.Expr) interface{} {
 			val, _ := v.Valuer.Meta(string(expr.StreamName) + ast.COLUMN_SEPARATOR + expr.Name)
 			return val
 		}
+	case *ast.JsonFieldRef:
+		val, ok := v.Valuer.Value(expr.Name)
+		if ok {
+			return val
+		} else {
+			return nil
+		}
 	case *ast.Wildcard:
 		val, _ := v.Valuer.Value("")
 		return val
@@ -515,7 +522,7 @@ func (v *ValuerEval) evalJsonExpr(result interface{}, op ast.Token, expr ast.Exp
 	case ast.ARROW:
 		if val, ok := result.(map[string]interface{}); ok {
 			switch e := expr.(type) {
-			case *ast.FieldRef, *ast.MetaRef:
+			case *ast.JsonFieldRef:
 				ve := &ValuerEval{Valuer: Message(val)}
 				return ve.Eval(e)
 			default:
diff --git a/pkg/ast/expr.go b/pkg/ast/expr.go
index 6b96549f..afaac878 100644
--- a/pkg/ast/expr.go
+++ b/pkg/ast/expr.go
@@ -192,9 +192,6 @@ func (fr *FieldRef) IsColumn() bool {
 func (fr *FieldRef) IsAlias() bool {
 	return fr.StreamName == AliasStream
 }
-func (fr *FieldRef) IsSQLField() bool {
-	return fr.StreamName != ""
-}
 
 func (fr *FieldRef) IsAggregate() bool {
 	if fr.StreamName != AliasStream {
@@ -278,3 +275,10 @@ type MetaRef struct {
 
 func (fr *MetaRef) expr() {}
 func (fr *MetaRef) node() {}
+
+type JsonFieldRef struct {
+	Name string
+}
+
+func (fr *JsonFieldRef) expr() {}
+func (fr *JsonFieldRef) node() {}


Test patch:
diff --git a/internal/topo/planner/analyzer_test.go b/internal/topo/planner/analyzer_test.go
index d33cba93..398404dc 100644
--- a/internal/topo/planner/analyzer_test.go
+++ b/internal/topo/planner/analyzer_test.go
@@ -116,6 +116,10 @@ var tests = []struct {
 		sql: `SELECT collect(*)[-1] as current FROM src1 GROUP BY COUNTWINDOW(2, 1) HAVING isNull(current->name) = false`,
 		r:   newErrorStruct(""),
 	},
+	{ // 14
+		sql: `SELECT sum(next->nid) as nid FROM src1 WHERE next->nid > 20 `,
+		r:   newErrorStruct(""),
+	},
 }
 
 func Test_validation(t *testing.T) {
@@ -128,7 +132,8 @@ func Test_validation(t *testing.T) {
 		"src1": `CREATE STREAM src1 (
 					id1 BIGINT,
 					temp BIGINT,
-					name string
+					name string,
+					next STRUCT(NAME STRING, NID BIGINT)
 				) WITH (DATASOURCE="src1", FORMAT="json", KEY="ts");`,
 	}
 	types := map[string]ast.StreamType{
diff --git a/internal/topo/planner/planner_test.go b/internal/topo/planner/planner_test.go
index 45be2c83..86a2509d 100644
--- a/internal/topo/planner/planner_test.go
+++ b/internal/topo/planner/planner_test.go
@@ -893,7 +893,7 @@ func Test_createLogicalPlan(t *testing.T) {
 								&ast.BinaryExpr{
 									OP:  ast.ARROW,
 									LHS: &ast.MetaRef{Name: "Humidity", StreamName: ast.DefaultStream},
-									RHS: &ast.MetaRef{Name: "Device"},
+									RHS: &ast.JsonFieldRef{Name: "Device"},
 								},
 							}},
 							[]ast.StreamName{},
@@ -1809,7 +1809,7 @@ func Test_createLogicalPlanSchemaless(t *testing.T) {
 								&ast.BinaryExpr{
 									OP:  ast.ARROW,
 									LHS: &ast.MetaRef{Name: "Humidity", StreamName: ast.DefaultStream},
-									RHS: &ast.MetaRef{Name: "Device"},
+									RHS: &ast.JsonFieldRef{Name: "Device"},
 								},
 							}},
 							[]ast.StreamName{},
diff --git a/internal/xsql/funcsAstValidator_test.go b/internal/xsql/funcsAstValidator_test.go
index 1a6a08a2..b24322ec 100644
--- a/internal/xsql/funcsAstValidator_test.go
+++ b/internal/xsql/funcsAstValidator_test.go
@@ -455,9 +455,9 @@ func TestFuncValidator(t *testing.T) {
 				LHS: &ast.BinaryExpr{
 					OP:  ast.ARROW,
 					LHS: &ast.MetaRef{Name: "device", StreamName: ast.DefaultStream},
-					RHS: &ast.MetaRef{Name: "reading"},
+					RHS: &ast.JsonFieldRef{Name: "reading"},
 				},
-				RHS: &ast.MetaRef{Name: "topic"},
+				RHS: &ast.JsonFieldRef{Name: "topic"},
 			}}}}}, Sources: []ast.Source{&ast.Table{Name: "tbl"}}},
 		},
 		{
diff --git a/internal/xsql/parser_test.go b/internal/xsql/parser_test.go
index eeeaee57..1f01ac2f 100644
--- a/internal/xsql/parser_test.go
+++ b/internal/xsql/parser_test.go
@@ -1718,7 +1718,7 @@ func TestParser_ParseJsonExpr(t *testing.T) {
 								RHS: &ast.IndexExpr{Index: &ast.IntegerLiteral{Val: 0}},
 							},
 							OP:  ast.ARROW,
-							RHS: &ast.FieldRef{Name: "first"},
+							RHS: &ast.JsonFieldRef{Name: "first"},
 						},
 
 						Name:  "",
@@ -1737,7 +1737,7 @@ func TestParser_ParseJsonExpr(t *testing.T) {
 							LHS: &ast.BinaryExpr{
 								LHS: &ast.FieldRef{Name: "children", StreamName: ast.DefaultStream},
 								OP:  ast.ARROW,
-								RHS: &ast.FieldRef{Name: "first"},
+								RHS: &ast.JsonFieldRef{Name: "first"},
 							},
 							OP:  ast.SUBSET,
 							RHS: &ast.IndexExpr{Index: &ast.IntegerLiteral{Val: 2}},
@@ -1760,13 +1760,13 @@ func TestParser_ParseJsonExpr(t *testing.T) {
 								LHS: &ast.BinaryExpr{
 									LHS: &ast.FieldRef{Name: "children", StreamName: ast.DefaultStream},
 									OP:  ast.ARROW,
-									RHS: &ast.FieldRef{Name: "first"},
+									RHS: &ast.JsonFieldRef{Name: "first"},
 								},
 								OP:  ast.SUBSET,
 								RHS: &ast.IndexExpr{Index: &ast.IntegerLiteral{Val: 2}},
 							},
 							OP:  ast.ARROW,
-							RHS: &ast.FieldRef{Name: "test"},
+							RHS: &ast.JsonFieldRef{Name: "test"},
 						},
 
 						Name:  "",
@@ -1852,7 +1852,7 @@ func TestParser_ParseJsonExpr(t *testing.T) {
 						Expr: &ast.BinaryExpr{
 							LHS: &ast.BinaryExpr{LHS: &ast.FieldRef{Name: "children", StreamName: ast.DefaultStream}, OP: ast.SUBSET, RHS: &ast.ColonExpr{Start: &ast.IntegerLiteral{Val: 2}, End: &ast.IntegerLiteral{Val: math.MinInt32}}},
 							OP:  ast.ARROW,
-							RHS: &ast.FieldRef{Name: "first"},
+							RHS: &ast.JsonFieldRef{Name: "first"},
 						},
 						Name:  "",
 						AName: "c"},
@@ -1882,7 +1882,7 @@ func TestParser_ParseJsonExpr(t *testing.T) {
 						Expr: &ast.BinaryExpr{
 							LHS: &ast.BinaryExpr{LHS: &ast.FieldRef{StreamName: ast.StreamName("demo"), Name: "children"}, OP: ast.SUBSET, RHS: &ast.ColonExpr{Start: &ast.IntegerLiteral{Val: 2}, End: &ast.IntegerLiteral{Val: math.MinInt32}}},
 							OP:  ast.ARROW,
-							RHS: &ast.FieldRef{Name: "first"},
+							RHS: &ast.JsonFieldRef{Name: "first"},
 						},
 						Name:  "",
 						AName: "c"},
@@ -1902,7 +1902,7 @@ func TestParser_ParseJsonExpr(t *testing.T) {
 								&ast.BinaryExpr{
 									LHS: &ast.BinaryExpr{LHS: &ast.FieldRef{StreamName: ast.StreamName("demo"), Name: "children"}, OP: ast.SUBSET, RHS: &ast.ColonExpr{Start: &ast.IntegerLiteral{Val: 2}, End: &ast.IntegerLiteral{Val: math.MinInt32}}},
 									OP:  ast.ARROW,
-									RHS: &ast.FieldRef{Name: "first"},
+									RHS: &ast.JsonFieldRef{Name: "first"},
 								},
 							},
 						},