diff --git a/sqle/driver/mysql/mysql_test.go b/sqle/driver/mysql/mysql_test.go index 8b98c0798..7ed440266 100644 --- a/sqle/driver/mysql/mysql_test.go +++ b/sqle/driver/mysql/mysql_test.go @@ -38,6 +38,12 @@ WHERE last_name = 'Smith';`) assert.NoError(t, err) assert.Len(t, nodes, 1) assert.Equal(t, nodes[0].Type, driverV2.SQLTypeDML) + + nodes, err = DefaultMysqlInspect().Parse(context.TODO(), + "SELECT id, ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary) AS rn FROM emp") + assert.NoError(t, err) + assert.Len(t, nodes, 1) + assert.Equal(t, driverV2.SQLTypeDQL, nodes[0].Type) } func TestInspect_onlineddlWithGhost(t *testing.T) { @@ -209,6 +215,11 @@ CREATE TABLE new_tbl AS SELECT * FROM orig_tbl;`, CREATEaa TABLE new_tbl AS SELECT * FROM orig_tbl;`, driverV2.SQLTypeDDL, }, + { + "case 8 window function select", + `SELECT id, ROW_NUMBER() OVER (PARTITION BY nominate_app_id ORDER BY create_date ASC) AS rn FROM sourcing.tt_nomi_record`, + driverV2.SQLTypeDQL, + }, } i := &MysqlDriverImpl{} for _, arg := range args { diff --git a/sqle/driver/mysql/rule_00082_test.go b/sqle/driver/mysql/rule_00082_test.go index 7b4329a61..e02691c18 100644 --- a/sqle/driver/mysql/rule_00082_test.go +++ b/sqle/driver/mysql/rule_00082_test.go @@ -74,20 +74,20 @@ func TestRuleSQLE00082(t *testing.T) { }, }, newTestResult()) - // case 5: SELECT 语句使用窗口函数可能触发 filesort // TODO 当前解析器解析不了此语法 - // runAIRuleCase(rule, t, "case 5: SELECT 语句使用窗口函数可能触发 filesort", - // "SELECT age FROM (SELECT age, ROW_NUMBER() OVER (PARTITION BY age ORDER BY age DESC) gn FROM customers) T WHERE gn=1 LIMIT 1;", - // session.NewAIMockContext().WithSQL("CREATE TABLE customers (id INT, age INT);"), - // []*AIMockSQLExpectation{ - // { - // Query: "EXPLAIN SELECT age FROM (SELECT age, ROW_NUMBER() OVER (PARTITION BY age ORDER BY age DESC) gn FROM customers) T WHERE gn=1 LIMIT 1;", - // Rows: sqlmock.NewRows([]string{"Extra"}).AddRow("Using filesort"), - // }, - // { - // Query: "SHOW WARNINGS", - // Rows: sqlmock.NewRows(nil), - // }, - // }, newTestResult().addResult(ruleName)) + // case 5: SELECT 语句使用窗口函数可能触发 filesort + runAIRuleCase(rule, t, "case 5: SELECT 语句使用窗口函数可能触发 filesort", + "SELECT age FROM (SELECT age, ROW_NUMBER() OVER (PARTITION BY age ORDER BY age DESC) gn FROM customers) T WHERE gn=1 LIMIT 1;", + session.NewAIMockContext().WithSQL("CREATE TABLE customers (id INT, age INT);"), + []*AIMockSQLExpectation{ + { + Query: "EXPLAIN SELECT age FROM (SELECT age, ROW_NUMBER() OVER (PARTITION BY age ORDER BY age DESC) gn FROM customers) T WHERE gn=1 LIMIT 1;", + Rows: sqlmock.NewRows([]string{"Extra"}).AddRow("Using filesort"), + }, + { + Query: "SHOW WARNINGS", + Rows: sqlmock.NewRows(nil), + }, + }, newTestResult().addResult(ruleName)) // case 6: SELECT 语句使用 GROUP BY 和 ORDER BY 不一致可能触发 filesort runAIRuleCase(rule, t, "case 6: SELECT 语句使用 GROUP BY 和 ORDER BY 不一致可能触发 filesort", diff --git a/sqle/driver/mysql/splitter/splitter.go b/sqle/driver/mysql/splitter/splitter.go index e83f84384..bf9d77fc0 100644 --- a/sqle/driver/mysql/splitter/splitter.go +++ b/sqle/driver/mysql/splitter/splitter.go @@ -16,8 +16,10 @@ type splitter struct { } func NewSplitter() *splitter { + p := parser.New() + p.EnableWindowFunc(true) return &splitter{ - parser: parser.New(), + parser: p, delimiter: NewDelimiter(), scanner: parser.NewScanner(""), } diff --git a/sqle/driver/mysql/splitter/splitter_test.go b/sqle/driver/mysql/splitter/splitter_test.go index b10d1f75c..ec94ad10d 100644 --- a/sqle/driver/mysql/splitter/splitter_test.go +++ b/sqle/driver/mysql/splitter/splitter_test.go @@ -249,6 +249,24 @@ Alter table point_trans_shard_00_part_202401 ADD CONSTRAINT chk_point_trans_shar } } +func TestParseWindowFunctionSelect(t *testing.T) { + parser := NewSplitter() + sql := "SELECT id, ROW_NUMBER() OVER (PARTITION BY nominate_app_id ORDER BY create_date ASC) AS rn FROM sourcing.tt_nomi_record WHERE is_delete = 0" + stmt, err := parser.ParseSqlText(sql) + if err != nil { + t.Fatal(err) + } + if len(stmt) != 1 { + t.Fatalf("expect 1 stmt, got %d", len(stmt)) + } + if _, ok := stmt[0].(*ast.UnparsedStmt); ok { + t.Fatal("expect SelectStmt, got UnparsedStmt") + } + if _, ok := stmt[0].(*ast.SelectStmt); !ok { + t.Fatalf("expect SelectStmt, got %T", stmt[0]) + } +} + func TestPerfectParse(t *testing.T) { parser := NewSplitter()