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) (#49499)

close #49377
  • Loading branch information
ti-chi-bot authored Feb 20, 2024
1 parent 4058740 commit a91305e
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 4 deletions.
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 @@ -4319,3 +4319,36 @@ func TestIssue38756(t *testing.T) {
tk.MustQuery("(SELECT DISTINCT SQRT(1) FROM t)").Check(testkit.Rows("1"))
tk.MustQuery("SELECT DISTINCT cast(1 as double) FROM t").Check(testkit.Rows("1"))
}

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 @@ -19879,15 +19879,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 @@ -10243,15 +10243,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
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 @@ -2109,6 +2109,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 @@ -1051,5 +1051,14 @@
"explain format = 'brief' select /*+ use_index(t, kj) */ * from t where json_contains(j, '[1, 2, 3]') and a=1 limit 1; -- index merge intersection case, sink limit to table side because selection exists on table side",
"explain format = 'brief' select /*+ use_index(t, kj) */ * from t where json_overlaps(j, '[1, 2, 3]') and a=1 limit 1; -- index merge union case, sink limit above selection above index merge reader, because json_overlaps can't be pushed down"
]
},
{
"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 @@ -7951,5 +7951,103 @@
]
}
]
},
{
"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 @@ -2124,6 +2124,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 @@ -2222,7 +2228,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 @@ -2246,7 +2252,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 a91305e

Please sign in to comment.