Browse Source

feat(sql): support wildcard with other selects

Signed-off-by: Jiyong Huang <huangjy@emqx.io>
Jiyong Huang 2 years atrás
parent
commit
3b4993b15e

+ 23 - 0
internal/topo/operator/project_test.go

@@ -582,6 +582,29 @@ func TestProjectPlan_Apply1(t *testing.T) {
 				"b": "true",
 				"b": "true",
 			}},
 			}},
 		},
 		},
+		//35
+		{
+			sql: `SELECT a->b AS ab, *, abs(f1) FROM test`,
+			data: &xsql.Tuple{
+				Emitter: "test",
+				Message: xsql.Message{
+					"a": map[string]interface{}{
+						"b": "test",
+					},
+					"b":  "b",
+					"f1": -12,
+				},
+			},
+			result: []map[string]interface{}{{
+				"a": map[string]interface{}{
+					"b": "test",
+				},
+				"ab":  "test",
+				"abs": 12,
+				"b":   "b",
+				"f1":  -12,
+			}},
+		},
 	}
 	}
 
 
 	fmt.Printf("The test bucket size is %d.\n\n", len(tests))
 	fmt.Printf("The test bucket size is %d.\n\n", len(tests))

+ 65 - 3
internal/topo/planner/planner_test.go

@@ -416,7 +416,7 @@ func Test_createLogicalPlan(t *testing.T) {
 				fields: []ast.Field{
 				fields: []ast.Field{
 					{
 					{
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
-						Name:  "",
+						Name:  "*",
 						AName: ""},
 						AName: ""},
 				},
 				},
 				isAggregate: false,
 				isAggregate: false,
@@ -1182,6 +1182,68 @@ func Test_createLogicalPlan(t *testing.T) {
 				sendMeta:    false,
 				sendMeta:    false,
 			}.Init(),
 			}.Init(),
 		},
 		},
+		{ // 14
+			sql: `SELECT name, *, meta(device) FROM src1`,
+			p: ProjectPlan{
+				baseLogicalPlan: baseLogicalPlan{
+					children: []LogicalPlan{
+						DataSourcePlan{
+							baseLogicalPlan: baseLogicalPlan{},
+							name:            "src1",
+							streamFields: []interface{}{
+								&ast.StreamField{
+									Name:      "id1",
+									FieldType: &ast.BasicType{Type: ast.BIGINT},
+								},
+								&ast.StreamField{
+									Name:      "temp",
+									FieldType: &ast.BasicType{Type: ast.BIGINT},
+								},
+								&ast.StreamField{
+									Name:      "name",
+									FieldType: &ast.BasicType{Type: ast.STRINGS},
+								},
+								&ast.StreamField{
+									Name:      "myarray",
+									FieldType: &ast.ArrayType{Type: ast.STRINGS},
+								},
+							},
+							streamStmt: streams["src1"],
+							metaFields: []string{"device"},
+							isWildCard: true,
+						}.Init(),
+					},
+				},
+				fields: []ast.Field{
+					{
+						Expr:  &ast.FieldRef{Name: "name", StreamName: "src1"},
+						Name:  "name",
+						AName: "",
+					},
+					{
+						Name: "*",
+						Expr: &ast.Wildcard{
+							Token: ast.ASTERISK,
+						},
+					},
+					{
+						Name: "meta",
+						Expr: &ast.Call{
+							Name: "meta",
+							Args: []ast.Expr{
+								&ast.MetaRef{
+									StreamName: ast.DefaultStream,
+									Name:       "device",
+								},
+							},
+						},
+					},
+				},
+				isAggregate: false,
+				allWildcard: true,
+				sendMeta:    false,
+			}.Init(),
+		},
 	}
 	}
 	fmt.Printf("The test bucket size is %d.\n\n", len(tests))
 	fmt.Printf("The test bucket size is %d.\n\n", len(tests))
 
 
@@ -1520,7 +1582,7 @@ func Test_createLogicalPlanSchemaless(t *testing.T) {
 				fields: []ast.Field{
 				fields: []ast.Field{
 					{
 					{
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
-						Name:  "",
+						Name:  "*",
 						AName: ""},
 						AName: ""},
 				},
 				},
 				isAggregate: false,
 				isAggregate: false,
@@ -2600,7 +2662,7 @@ func Test_createLogicalPlan4Lookup(t *testing.T) {
 				fields: []ast.Field{
 				fields: []ast.Field{
 					{
 					{
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
-						Name:  "",
+						Name:  "*",
 						AName: "",
 						AName: "",
 					},
 					},
 				},
 				},

+ 6 - 1
internal/topo/topotest/rule_test.go

@@ -29,32 +29,37 @@ func TestSingleSQL(t *testing.T) {
 	var tests = []RuleTest{
 	var tests = []RuleTest{
 		{
 		{
 			Name: `TestSingleSQLRule1`,
 			Name: `TestSingleSQLRule1`,
-			Sql:  `SELECT * FROM demo`,
+			Sql:  `SELECT *, upper(color) FROM demo`,
 			R: [][]map[string]interface{}{
 			R: [][]map[string]interface{}{
 				{{
 				{{
 					"color": "red",
 					"color": "red",
 					"size":  float64(3),
 					"size":  float64(3),
 					"ts":    float64(1541152486013),
 					"ts":    float64(1541152486013),
+					"upper": "RED",
 				}},
 				}},
 				{{
 				{{
 					"color": "blue",
 					"color": "blue",
 					"size":  float64(6),
 					"size":  float64(6),
 					"ts":    float64(1541152486822),
 					"ts":    float64(1541152486822),
+					"upper": "BLUE",
 				}},
 				}},
 				{{
 				{{
 					"color": "blue",
 					"color": "blue",
 					"size":  float64(2),
 					"size":  float64(2),
 					"ts":    float64(1541152487632),
 					"ts":    float64(1541152487632),
+					"upper": "BLUE",
 				}},
 				}},
 				{{
 				{{
 					"color": "yellow",
 					"color": "yellow",
 					"size":  float64(4),
 					"size":  float64(4),
 					"ts":    float64(1541152488442),
 					"ts":    float64(1541152488442),
+					"upper": "YELLOW",
 				}},
 				}},
 				{{
 				{{
 					"color": "red",
 					"color": "red",
 					"size":  float64(1),
 					"size":  float64(1),
 					"ts":    float64(1541152489252),
 					"ts":    float64(1541152489252),
+					"upper": "RED",
 				}},
 				}},
 			},
 			},
 			M: map[string]interface{}{
 			M: map[string]interface{}{

+ 6 - 10
internal/xsql/parser.go

@@ -411,13 +411,6 @@ func (p *Parser) parseSorts() (ast.SortFields, error) {
 func (p *Parser) parseFields() (ast.Fields, error) {
 func (p *Parser) parseFields() (ast.Fields, error) {
 	var fields ast.Fields
 	var fields ast.Fields
 
 
-	tok, _ := p.scanIgnoreWhitespace()
-	if tok == ast.ASTERISK {
-		fields = append(fields, ast.Field{AName: "", Expr: &ast.Wildcard{Token: tok}})
-		return fields, nil
-	}
-	p.unscan()
-
 	for {
 	for {
 		field, err := p.parseField()
 		field, err := p.parseField()
 
 
@@ -427,7 +420,7 @@ func (p *Parser) parseFields() (ast.Fields, error) {
 			fields = append(fields, *field)
 			fields = append(fields, *field)
 		}
 		}
 
 
-		tok, _ = p.scanIgnoreWhitespace()
+		tok, _ := p.scanIgnoreWhitespace()
 		if tok != ast.COMMA {
 		if tok != ast.COMMA {
 			p.unscan()
 			p.unscan()
 			break
 			break
@@ -449,6 +442,9 @@ func (p *Parser) parseField() (*ast.Field, error) {
 		return nil, err
 		return nil, err
 	} else {
 	} else {
 		if alias != "" {
 		if alias != "" {
+			if field.Name == "*" {
+				return nil, fmt.Errorf("alias is not supported for *")
+			}
 			field.AName = alias
 			field.AName = alias
 		}
 		}
 	}
 	}
@@ -466,6 +462,8 @@ func nameExpr(exp ast.Expr) string {
 		return e.Name
 		return e.Name
 	case *ast.Call:
 	case *ast.Call:
 		return e.Name
 		return e.Name
+	case *ast.Wildcard:
+		return ast.Tokens[ast.ASTERISK]
 	default:
 	default:
 		return ""
 		return ""
 	}
 	}
@@ -1487,8 +1485,6 @@ func (p *Parser) parseAsterisk() (ast.Expr, error) {
 	switch p.inFunc {
 	switch p.inFunc {
 	case "mqtt", "meta":
 	case "mqtt", "meta":
 		return &ast.MetaRef{StreamName: ast.DefaultStream, Name: "*"}, nil
 		return &ast.MetaRef{StreamName: ast.DefaultStream, Name: "*"}, nil
-	case "":
-		return nil, fmt.Errorf("unsupported * expression, it must be used inside fields or function parameters.")
 	default:
 	default:
 		return &ast.Wildcard{Token: ast.ASTERISK}, nil
 		return &ast.Wildcard{Token: ast.ASTERISK}, nil
 	}
 	}

+ 90 - 13
internal/xsql/parser_test.go

@@ -194,7 +194,7 @@ func TestParser_ParseStatement(t *testing.T) {
 				Fields: []ast.Field{
 				Fields: []ast.Field{
 					{
 					{
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
-						Name:  "",
+						Name:  "*",
 						AName: ""},
 						AName: ""},
 				},
 				},
 				Sources: []ast.Source{&ast.Table{Name: "tbl"}},
 				Sources: []ast.Source{&ast.Table{Name: "tbl"}},
@@ -524,13 +524,13 @@ func TestParser_ParseStatement(t *testing.T) {
 		{
 		{
 			s:    `SELECT * AS alias FROM tbl`,
 			s:    `SELECT * AS alias FROM tbl`,
 			stmt: nil,
 			stmt: nil,
-			err:  `found "AS", expected FROM.`,
+			err:  `alias is not supported for *`,
 		},
 		},
 
 
 		{
 		{
 			s:    `SELECT *, FROM tbl`,
 			s:    `SELECT *, FROM tbl`,
 			stmt: nil,
 			stmt: nil,
-			err:  `found ",", expected FROM.`,
+			err:  `found "FROM", expected expression.`,
 		},
 		},
 
 
 		{
 		{
@@ -1305,7 +1305,7 @@ func TestParser_ParseStatement(t *testing.T) {
 				Fields: []ast.Field{
 				Fields: []ast.Field{
 					{
 					{
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
-						Name:  "",
+						Name:  "*",
 						AName: ""},
 						AName: ""},
 				},
 				},
 				Sources:    []ast.Source{&ast.Table{Name: "topic/sensor1"}},
 				Sources:    []ast.Source{&ast.Table{Name: "topic/sensor1"}},
@@ -1319,7 +1319,7 @@ func TestParser_ParseStatement(t *testing.T) {
 				Fields: []ast.Field{
 				Fields: []ast.Field{
 					{
 					{
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
-						Name:  "",
+						Name:  "*",
 						AName: ""},
 						AName: ""},
 				},
 				},
 				Sources:    []ast.Source{&ast.Table{Name: "topic/sensor1"}},
 				Sources:    []ast.Source{&ast.Table{Name: "topic/sensor1"}},
@@ -1333,7 +1333,7 @@ func TestParser_ParseStatement(t *testing.T) {
 				Fields: []ast.Field{
 				Fields: []ast.Field{
 					{
 					{
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
-						Name:  "",
+						Name:  "*",
 						AName: ""},
 						AName: ""},
 				},
 				},
 				Sources: []ast.Source{&ast.Table{Name: "topic/sensor1"}},
 				Sources: []ast.Source{&ast.Table{Name: "topic/sensor1"}},
@@ -1752,7 +1752,7 @@ func TestParser_ParseStatement(t *testing.T) {
 									StreamName: ast.DefaultStream,
 									StreamName: ast.DefaultStream,
 									Name:       "a",
 									Name:       "a",
 								}},
 								}},
-								&ast.ColFuncField{Name: "", Expr: &ast.Wildcard{
+								&ast.ColFuncField{Name: "*", Expr: &ast.Wildcard{
 									Token: ast.ASTERISK,
 									Token: ast.ASTERISK,
 								}},
 								}},
 								&ast.ColFuncField{Name: "c", Expr: &ast.FieldRef{
 								&ast.ColFuncField{Name: "c", Expr: &ast.FieldRef{
@@ -1878,6 +1878,83 @@ func TestParser_ParseStatement(t *testing.T) {
 				Sources: []ast.Source{&ast.Table{Name: "tbl"}},
 				Sources: []ast.Source{&ast.Table{Name: "tbl"}},
 			},
 			},
 		},
 		},
+		{
+			s: `SELECT *, name, lower(name) as ln FROM tbl`,
+			stmt: &ast.SelectStatement{
+				Fields: []ast.Field{
+					{
+						Expr: &ast.Wildcard{
+							Token: ast.ASTERISK,
+						},
+						Name: "*",
+					},
+					{
+						Expr: &ast.FieldRef{
+							Name:       "name",
+							StreamName: ast.DefaultStream,
+						},
+						Name:  "name",
+						AName: "",
+					},
+					{
+						Expr: &ast.Call{
+							Name:   "lower",
+							FuncId: 0,
+							Args: []ast.Expr{
+								&ast.FieldRef{Name: "name", StreamName: ast.DefaultStream},
+							},
+						},
+						Name:  "lower",
+						AName: "ln",
+					},
+				},
+				Sources: []ast.Source{&ast.Table{Name: "tbl"}},
+			},
+		},
+		{
+			s: `SELECT name, * FROM tbl`,
+			stmt: &ast.SelectStatement{
+				Fields: []ast.Field{
+					{
+						Expr: &ast.FieldRef{
+							Name:       "name",
+							StreamName: ast.DefaultStream,
+						},
+						Name:  "name",
+						AName: "",
+					},
+					{
+						Expr: &ast.Wildcard{
+							Token: ast.ASTERISK,
+						},
+						Name: "*",
+					},
+				},
+				Sources: []ast.Source{&ast.Table{Name: "tbl"}},
+			},
+		},
+		{
+			s: `SELECT name, * FROM tbl`,
+			stmt: &ast.SelectStatement{
+				Fields: []ast.Field{
+					{
+						Expr: &ast.FieldRef{
+							Name:       "name",
+							StreamName: ast.DefaultStream,
+						},
+						Name:  "name",
+						AName: "",
+					},
+					{
+						Expr: &ast.Wildcard{
+							Token: ast.ASTERISK,
+						},
+						Name: "*",
+					},
+				},
+				Sources: []ast.Source{&ast.Table{Name: "tbl"}},
+			},
+		},
 	}
 	}
 
 
 	fmt.Printf("The test bucket size is %d.\n\n", len(tests))
 	fmt.Printf("The test bucket size is %d.\n\n", len(tests))
@@ -2052,7 +2129,7 @@ func TestParser_ParseWindowsExpr(t *testing.T) {
 				Fields: []ast.Field{
 				Fields: []ast.Field{
 					{
 					{
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
-						Name:  "",
+						Name:  "*",
 						AName: ""},
 						AName: ""},
 				},
 				},
 				Sources: []ast.Source{&ast.Table{Name: "demo"}},
 				Sources: []ast.Source{&ast.Table{Name: "demo"}},
@@ -2078,7 +2155,7 @@ func TestParser_ParseWindowsExpr(t *testing.T) {
 				Fields: []ast.Field{
 				Fields: []ast.Field{
 					{
 					{
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
-						Name:  "",
+						Name:  "*",
 						AName: ""},
 						AName: ""},
 				},
 				},
 				Sources: []ast.Source{&ast.Table{Name: "demo"}},
 				Sources: []ast.Source{&ast.Table{Name: "demo"}},
@@ -2107,7 +2184,7 @@ func TestParser_ParseWindowsExpr(t *testing.T) {
 				Fields: []ast.Field{
 				Fields: []ast.Field{
 					{
 					{
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
-						Name:  "",
+						Name:  "*",
 						AName: ""},
 						AName: ""},
 				},
 				},
 				Sources: []ast.Source{&ast.Table{Name: "demo"}},
 				Sources: []ast.Source{&ast.Table{Name: "demo"}},
@@ -2493,7 +2570,7 @@ func TestParser_ParseJoins(t *testing.T) {
 				Fields: []ast.Field{
 				Fields: []ast.Field{
 					{
 					{
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
-						Name:  "",
+						Name:  "*",
 						AName: ""},
 						AName: ""},
 				},
 				},
 				Sources: []ast.Source{&ast.Table{Name: "topic/sensor1"}},
 				Sources: []ast.Source{&ast.Table{Name: "topic/sensor1"}},
@@ -2515,7 +2592,7 @@ func TestParser_ParseJoins(t *testing.T) {
 				Fields: []ast.Field{
 				Fields: []ast.Field{
 					{
 					{
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
-						Name:  "",
+						Name:  "*",
 						AName: ""},
 						AName: ""},
 				},
 				},
 				Sources: []ast.Source{&ast.Table{Name: "topic/sensor1", Alias: "t1"}},
 				Sources: []ast.Source{&ast.Table{Name: "topic/sensor1", Alias: "t1"}},
@@ -2537,7 +2614,7 @@ func TestParser_ParseJoins(t *testing.T) {
 				Fields: []ast.Field{
 				Fields: []ast.Field{
 					{
 					{
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
 						Expr:  &ast.Wildcard{Token: ast.ASTERISK},
-						Name:  "",
+						Name:  "*",
 						AName: ""},
 						AName: ""},
 				},
 				},
 				Sources: []ast.Source{&ast.Table{Name: "topic/sensor1", Alias: "t1"}},
 				Sources: []ast.Source{&ast.Table{Name: "topic/sensor1", Alias: "t1"}},