Skip to content

Commit

Permalink
parser, planner: fix embedded setOprStmt will be seen as SetOprSelect…
Browse files Browse the repository at this point in the history
…List item and lost its orderBy and Limit (#49421)

close #49377
  • Loading branch information
AilinKid authored Dec 15, 2023
1 parent a372fc0 commit 4238341
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 6 deletions.
2 changes: 1 addition & 1 deletion pkg/executor/test/executor/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ go_test(
"main_test.go",
],
flaky = True,
shard_count = 46,
shard_count = 47,
deps = [
"//pkg/config",
"//pkg/ddl",
Expand Down
33 changes: 33 additions & 0 deletions pkg/executor/test/executor/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2724,3 +2724,36 @@ func TestProcessInfoOfSubQuery(t *testing.T) {
tk2.MustQuery("select 1 from information_schema.processlist where TxnStart != '' and info like 'select%sleep% from t%'").Check(testkit.Rows("1"))
wg.Wait()
}

func TestIssues49377(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("create table employee (employee_id int, name varchar(20), dept_id int)")
tk.MustExec("insert into employee values (1, 'Furina', 1), (2, 'Klee', 1), (3, 'Eula', 1), (4, 'Diluc', 2), (5, 'Tartaglia', 2)")

tk.MustQuery("select 1,1,1 union all ( " +
"(select * from employee where dept_id = 1) " +
"union all " +
"(select * from employee where dept_id = 1 order by employee_id) " +
"order by 1 limit 1 " +
");").Sort().Check(testkit.Rows("1 1 1", "1 Furina 1"))

tk.MustQuery("select 1,1,1 union all ( " +
"(select * from employee where dept_id = 1) " +
"union all " +
"(select * from employee where dept_id = 1 order by employee_id) " +
"order by 1" +
");").Sort().Check(testkit.Rows("1 1 1", "1 Furina 1", "1 Furina 1", "2 Klee 1", "2 Klee 1", "3 Eula 1", "3 Eula 1"))

tk.MustQuery("select * from employee where dept_id = 1 " +
"union all " +
"(select * from employee where dept_id = 1 order by employee_id) " +
"union all" +
"(" +
"select * from employee where dept_id = 1 " +
"union all " +
"(select * from employee where dept_id = 1 order by employee_id) " +
"limit 1" +
");").Sort().Check(testkit.Rows("1 Furina 1", "1 Furina 1", "1 Furina 1", "2 Klee 1", "2 Klee 1", "3 Eula 1", "3 Eula 1"))
}
2 changes: 2 additions & 0 deletions pkg/parser/ast/dml.go
Original file line number Diff line number Diff line change
Expand Up @@ -1557,6 +1557,8 @@ type SetOprSelectList struct {
With *WithClause
AfterSetOperator *SetOprType
Selects []Node
Limit *Limit
OrderBy *OrderByClause
}

// Restore implements Node interface.
Expand Down
8 changes: 7 additions & 1 deletion pkg/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -19948,15 +19948,21 @@ yynewstate:
}
var setOprList2 []ast.Node
var with2 *ast.WithClause
var limit2 *ast.Limit
var orderBy2 *ast.OrderByClause
switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) {
case *ast.SelectStmt:
setOprList2 = []ast.Node{x}
with2 = x.With
case *ast.SetOprStmt:
// child setOprStmt's limit and order should also make sense
// we should separate it out from other normal SetOprSelectList.
setOprList2 = x.SelectList.Selects
with2 = x.With
limit2 = x.Limit
orderBy2 = x.OrderBy
}
nextSetOprList := &ast.SetOprSelectList{Selects: setOprList2, With: with2}
nextSetOprList := &ast.SetOprSelectList{Selects: setOprList2, With: with2, Limit: limit2, OrderBy: orderBy2}
nextSetOprList.AfterSetOperator = yyS[yypt-1].item.(*ast.SetOprType)
setOprList := append(setOprList1, nextSetOprList)
setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: setOprList}}
Expand Down
8 changes: 7 additions & 1 deletion pkg/parser/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -10253,15 +10253,21 @@ SetOprStmtWoutLimitOrderBy:
}
var setOprList2 []ast.Node
var with2 *ast.WithClause
var limit2 *ast.Limit
var orderBy2 *ast.OrderByClause
switch x := $3.(*ast.SubqueryExpr).Query.(type) {
case *ast.SelectStmt:
setOprList2 = []ast.Node{x}
with2 = x.With
case *ast.SetOprStmt:
// child setOprStmt's limit and order should also make sense
// we should separate it out from other normal SetOprSelectList.
setOprList2 = x.SelectList.Selects
with2 = x.With
limit2 = x.Limit
orderBy2 = x.OrderBy
}
nextSetOprList := &ast.SetOprSelectList{Selects: setOprList2, With: with2}
nextSetOprList := &ast.SetOprSelectList{Selects: setOprList2, With: with2, Limit: limit2, OrderBy: orderBy2}
nextSetOprList.AfterSetOperator = $2.(*ast.SetOprType)
setOprList := append(setOprList1, nextSetOprList)
setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: setOprList}}
Expand Down
2 changes: 1 addition & 1 deletion pkg/planner/core/casetest/physicalplantest/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ go_test(
data = glob(["testdata/**"]),
flaky = True,
race = "on",
shard_count = 30,
shard_count = 31,
deps = [
"//pkg/config",
"//pkg/domain",
Expand Down
26 changes: 26 additions & 0 deletions pkg/planner/core/casetest/physicalplantest/physical_plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1352,6 +1352,32 @@ func TestCountStarForTiFlash(t *testing.T) {
}
}

func TestIssues49377Plan(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("drop table if exists employee")
tk.MustExec("create table employee (employee_id int, name varchar(20), dept_id int)")

var (
input []string
output []struct {
SQL string
Plan []string
Warning []string
}
)
planSuiteData := GetPlanSuiteData()
planSuiteData.LoadTestCases(t, &input, &output)
for i, ts := range input {
testdata.OnRecord(func() {
output[i].SQL = ts
output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows())
})
tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...))
}
}

func TestHashAggPushdownToTiFlashCompute(t *testing.T) {
var (
input []string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -594,5 +594,14 @@
"select /*+ agg_to_cop() hash_agg() */ count(1) from tbl_15 ;",
"select /*+ agg_to_cop() stream_agg() */ avg( tbl_16.col_100 ) as r0 from tbl_16 where tbl_16.col_100 in ( 10672141 ) or tbl_16.col_104 in ( 'yfEG1t!*b' ,'C1*bqx_qyO' ,'vQ^yUpKHr&j#~' ) group by tbl_16.col_100 order by r0 limit 20 ;"
]
},
{
"name": "TestIssues49377Plan",
"cases": [
"select 1,1,1 union all ((select * from employee where dept_id = 1) union all ( select * from employee where dept_id = 1 order by employee_id ) order by 1 );",
"select 1,1,1 union all ((select * from employee where dept_id = 1) union all ( select * from employee where dept_id = 1 order by employee_id ) order by 1 limit 1);",
"select * from employee where dept_id = 1 union all ( select * from employee where dept_id = 1 order by employee_id) union all ( select * from employee where dept_id = 1 union all ( select * from employee where dept_id = 1 order by employee_id ) limit 1);",
"select * from employee where dept_id = 1 union all ( select * from employee where dept_id = 1 order by employee_id) union all ( select * from employee where dept_id = 1 union all ( select * from employee where dept_id = 1 order by employee_id ) order by 1 limit 1);"
]
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -3621,5 +3621,103 @@
"Warning": null
}
]
},
{
"Name": "TestIssues49377Plan",
"Cases": [
{
"SQL": "select 1,1,1 union all ((select * from employee where dept_id = 1) union all ( select * from employee where dept_id = 1 order by employee_id ) order by 1 );",
"Plan": [
"Union 21.00 root ",
"├─Projection 1.00 root 1->Column#15, 1->Column#16, 1->Column#17",
"│ └─TableDual 1.00 root rows:1",
"└─Projection 20.00 root cast(Column#12, bigint(11) BINARY)->Column#15, Column#13->Column#16, cast(Column#14, bigint(11) BINARY)->Column#17",
" └─Sort 20.00 root Column#12",
" └─Union 20.00 root ",
" ├─TableReader 10.00 root data:Selection",
" │ └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
" │ └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo",
" └─Sort 10.00 root test.employee.employee_id",
" └─TableReader 10.00 root data:Selection",
" └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
" └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo"
],
"Warning": null
},
{
"SQL": "select 1,1,1 union all ((select * from employee where dept_id = 1) union all ( select * from employee where dept_id = 1 order by employee_id ) order by 1 limit 1);",
"Plan": [
"Union 2.00 root ",
"├─Projection 1.00 root 1->Column#15, 1->Column#16, 1->Column#17",
"│ └─TableDual 1.00 root rows:1",
"└─Projection 1.00 root cast(Column#12, bigint(11) BINARY)->Column#15, Column#13->Column#16, cast(Column#14, bigint(11) BINARY)->Column#17",
" └─TopN 1.00 root Column#12, offset:0, count:1",
" └─Union 2.00 root ",
" ├─TopN 1.00 root test.employee.employee_id, offset:0, count:1",
" │ └─TableReader 1.00 root data:TopN",
" │ └─TopN 1.00 cop[tikv] test.employee.employee_id, offset:0, count:1",
" │ └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
" │ └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo",
" └─TopN 1.00 root test.employee.employee_id, offset:0, count:1",
" └─TableReader 1.00 root data:TopN",
" └─TopN 1.00 cop[tikv] test.employee.employee_id, offset:0, count:1",
" └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
" └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo"
],
"Warning": null
},
{
"SQL": "select * from employee where dept_id = 1 union all ( select * from employee where dept_id = 1 order by employee_id) union all ( select * from employee where dept_id = 1 union all ( select * from employee where dept_id = 1 order by employee_id ) limit 1);",
"Plan": [
"Union 21.00 root ",
"├─TableReader 10.00 root data:Selection",
"│ └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
"│ └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo",
"├─Sort 10.00 root test.employee.employee_id",
"│ └─TableReader 10.00 root data:Selection",
"│ └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
"│ └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo",
"└─Limit 1.00 root offset:0, count:1",
" └─Union 1.00 root ",
" ├─Limit 1.00 root offset:0, count:1",
" │ └─TableReader 1.00 root data:Limit",
" │ └─Limit 1.00 cop[tikv] offset:0, count:1",
" │ └─Selection 1.00 cop[tikv] eq(test.employee.dept_id, 1)",
" │ └─TableFullScan 1000.00 cop[tikv] table:employee keep order:false, stats:pseudo",
" └─TopN 1.00 root test.employee.employee_id, offset:0, count:1",
" └─TableReader 1.00 root data:TopN",
" └─TopN 1.00 cop[tikv] test.employee.employee_id, offset:0, count:1",
" └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
" └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo"
],
"Warning": null
},
{
"SQL": "select * from employee where dept_id = 1 union all ( select * from employee where dept_id = 1 order by employee_id) union all ( select * from employee where dept_id = 1 union all ( select * from employee where dept_id = 1 order by employee_id ) order by 1 limit 1);",
"Plan": [
"Union 21.00 root ",
"├─TableReader 10.00 root data:Selection",
"│ └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
"│ └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo",
"├─Sort 10.00 root test.employee.employee_id",
"│ └─TableReader 10.00 root data:Selection",
"│ └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
"│ └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo",
"└─TopN 1.00 root Column#17, offset:0, count:1",
" └─Union 2.00 root ",
" ├─TopN 1.00 root test.employee.employee_id, offset:0, count:1",
" │ └─TableReader 1.00 root data:TopN",
" │ └─TopN 1.00 cop[tikv] test.employee.employee_id, offset:0, count:1",
" │ └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
" │ └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo",
" └─TopN 1.00 root test.employee.employee_id, offset:0, count:1",
" └─TableReader 1.00 root data:TopN",
" └─TopN 1.00 cop[tikv] test.employee.employee_id, offset:0, count:1",
" └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
" └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo"
],
"Warning": null
}
]
}
]
10 changes: 8 additions & 2 deletions pkg/planner/core/logical_plan_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2078,6 +2078,12 @@ func (b *PlanBuilder) buildSetOpr(ctx context.Context, setOpr *ast.SetOprStmt) (
if *x.AfterSetOperator != ast.Intersect && *x.AfterSetOperator != ast.IntersectAll {
breakIteration = true
}
if x.Limit != nil || x.OrderBy != nil {
// when SetOprSelectList's limit and order-by is not nil, it means itself is converted from
// an independent ast.SetOprStmt in parser, its data should be evaluated first, and ordered
// by given items and conduct a limit on it, then it can only be integrated with other brothers.
breakIteration = true
}
}
if breakIteration {
break
Expand Down Expand Up @@ -2176,7 +2182,7 @@ func (b *PlanBuilder) buildIntersect(ctx context.Context, selects []ast.Node) (L
leftPlan, err = b.buildSelect(ctx, x)
case *ast.SetOprSelectList:
afterSetOperator = x.AfterSetOperator
leftPlan, err = b.buildSetOpr(ctx, &ast.SetOprStmt{SelectList: x, With: x.With})
leftPlan, err = b.buildSetOpr(ctx, &ast.SetOprStmt{SelectList: x, With: x.With, Limit: x.Limit, OrderBy: x.OrderBy})
}
if err != nil {
return nil, nil, err
Expand All @@ -2200,7 +2206,7 @@ func (b *PlanBuilder) buildIntersect(ctx context.Context, selects []ast.Node) (L
// TODO: support intersect all
return nil, nil, errors.Errorf("TiDB do not support intersect all")
}
rightPlan, err = b.buildSetOpr(ctx, &ast.SetOprStmt{SelectList: x, With: x.With})
rightPlan, err = b.buildSetOpr(ctx, &ast.SetOprStmt{SelectList: x, With: x.With, Limit: x.Limit, OrderBy: x.OrderBy})
}
if err != nil {
return nil, nil, err
Expand Down

0 comments on commit 4238341

Please sign in to comment.