Skip to content

Commit 0882912

Browse files
authored
parser, planner: fix embedded setOprStmt will be seen as SetOprSelectList item and lost its orderBy and Limit (#49421) (#49502)
close #49377
1 parent 2bdadae commit 0882912

File tree

8 files changed

+192
-5
lines changed

8 files changed

+192
-5
lines changed

executor/explain_test.go

+34
Original file line numberDiff line numberDiff line change
@@ -569,3 +569,37 @@ func TestExplainJSON(t *testing.T) {
569569
}
570570
}
571571
}
572+
573+
func TestIssues49377(t *testing.T) {
574+
store := testkit.CreateMockStore(t)
575+
tk := testkit.NewTestKit(t, store)
576+
tk.MustExec("use test")
577+
tk.MustExec("drop table if exists employee")
578+
tk.MustExec("create table employee (employee_id int, name varchar(20), dept_id int)")
579+
tk.MustExec("insert into employee values (1, 'Furina', 1), (2, 'Klee', 1), (3, 'Eula', 1), (4, 'Diluc', 2), (5, 'Tartaglia', 2)")
580+
581+
tk.MustQuery("select 1,1,1 union all ( " +
582+
"(select * from employee where dept_id = 1) " +
583+
"union all " +
584+
"(select * from employee where dept_id = 1 order by employee_id) " +
585+
"order by 1 limit 1 " +
586+
");").Sort().Check(testkit.Rows("1 1 1", "1 Furina 1"))
587+
588+
tk.MustQuery("select 1,1,1 union all ( " +
589+
"(select * from employee where dept_id = 1) " +
590+
"union all " +
591+
"(select * from employee where dept_id = 1 order by employee_id) " +
592+
"order by 1" +
593+
");").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"))
594+
595+
tk.MustQuery("select * from employee where dept_id = 1 " +
596+
"union all " +
597+
"(select * from employee where dept_id = 1 order by employee_id) " +
598+
"union all" +
599+
"(" +
600+
"select * from employee where dept_id = 1 " +
601+
"union all " +
602+
"(select * from employee where dept_id = 1 order by employee_id) " +
603+
"limit 1" +
604+
");").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"))
605+
}

parser/ast/dml.go

+2
Original file line numberDiff line numberDiff line change
@@ -1540,6 +1540,8 @@ type SetOprSelectList struct {
15401540
With *WithClause
15411541
AfterSetOperator *SetOprType
15421542
Selects []Node
1543+
Limit *Limit
1544+
OrderBy *OrderByClause
15431545
}
15441546

15451547
// Restore implements Node interface.

parser/parser.go

+7-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

parser/parser.y

+7-1
Original file line numberDiff line numberDiff line change
@@ -9754,15 +9754,21 @@ SetOprStmtWoutLimitOrderBy:
97549754
}
97559755
var setOprList2 []ast.Node
97569756
var with2 *ast.WithClause
9757+
var limit2 *ast.Limit
9758+
var orderBy2 *ast.OrderByClause
97579759
switch x := $3.(*ast.SubqueryExpr).Query.(type) {
97589760
case *ast.SelectStmt:
97599761
setOprList2 = []ast.Node{x}
97609762
with2 = x.With
97619763
case *ast.SetOprStmt:
9764+
// child setOprStmt's limit and order should also make sense
9765+
// we should separate it out from other normal SetOprSelectList.
97629766
setOprList2 = x.SelectList.Selects
97639767
with2 = x.With
9768+
limit2 = x.Limit
9769+
orderBy2 = x.OrderBy
97649770
}
9765-
nextSetOprList := &ast.SetOprSelectList{Selects: setOprList2, With: with2}
9771+
nextSetOprList := &ast.SetOprSelectList{Selects: setOprList2, With: with2, Limit: limit2, OrderBy: orderBy2}
97669772
nextSetOprList.AfterSetOperator = $2.(*ast.SetOprType)
97679773
setOprList := append(setOprList1, nextSetOprList)
97689774
setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: setOprList}}

planner/core/logical_plan_builder.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -1818,6 +1818,12 @@ func (b *PlanBuilder) buildSetOpr(ctx context.Context, setOpr *ast.SetOprStmt) (
18181818
if *x.AfterSetOperator != ast.Intersect && *x.AfterSetOperator != ast.IntersectAll {
18191819
breakIteration = true
18201820
}
1821+
if x.Limit != nil || x.OrderBy != nil {
1822+
// when SetOprSelectList's limit and order-by is not nil, it means itself is converted from
1823+
// an independent ast.SetOprStmt in parser, its data should be evaluated first, and ordered
1824+
// by given items and conduct a limit on it, then it can only be integrated with other brothers.
1825+
breakIteration = true
1826+
}
18211827
}
18221828
if breakIteration {
18231829
break
@@ -1916,7 +1922,7 @@ func (b *PlanBuilder) buildIntersect(ctx context.Context, selects []ast.Node) (L
19161922
leftPlan, err = b.buildSelect(ctx, x)
19171923
case *ast.SetOprSelectList:
19181924
afterSetOperator = x.AfterSetOperator
1919-
leftPlan, err = b.buildSetOpr(ctx, &ast.SetOprStmt{SelectList: x, With: x.With})
1925+
leftPlan, err = b.buildSetOpr(ctx, &ast.SetOprStmt{SelectList: x, With: x.With, Limit: x.Limit, OrderBy: x.OrderBy})
19201926
}
19211927
if err != nil {
19221928
return nil, nil, err
@@ -1940,7 +1946,7 @@ func (b *PlanBuilder) buildIntersect(ctx context.Context, selects []ast.Node) (L
19401946
// TODO: support intersect all
19411947
return nil, nil, errors.Errorf("TiDB do not support intersect all")
19421948
}
1943-
rightPlan, err = b.buildSetOpr(ctx, &ast.SetOprStmt{SelectList: x, With: x.With})
1949+
rightPlan, err = b.buildSetOpr(ctx, &ast.SetOprStmt{SelectList: x, With: x.With, Limit: x.Limit, OrderBy: x.OrderBy})
19441950
}
19451951
if err != nil {
19461952
return nil, nil, err
@@ -7273,7 +7279,7 @@ func (b *PlanBuilder) buildRecursiveCTE(ctx context.Context, cte ast.ResultSetNo
72737279
p, err = b.buildSelect(ctx, y)
72747280
afterOpr = y.AfterSetOperator
72757281
case *ast.SetOprSelectList:
7276-
p, err = b.buildSetOpr(ctx, &ast.SetOprStmt{SelectList: y, With: y.With})
7282+
p, err = b.buildSetOpr(ctx, &ast.SetOprStmt{SelectList: y, With: y.With, Limit: y.Limit, OrderBy: y.OrderBy})
72777283
afterOpr = y.AfterSetOperator
72787284
}
72797285

planner/core/physical_plan_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -2562,3 +2562,29 @@ func TestIndexMergeOrderPushDown(t *testing.T) {
25622562
tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...))
25632563
}
25642564
}
2565+
2566+
func TestIssues49377Plan(t *testing.T) {
2567+
store := testkit.CreateMockStore(t)
2568+
tk := testkit.NewTestKit(t, store)
2569+
tk.MustExec("use test")
2570+
tk.MustExec("drop table if exists employee")
2571+
tk.MustExec("create table employee (employee_id int, name varchar(20), dept_id int)")
2572+
2573+
var (
2574+
input []string
2575+
output []struct {
2576+
SQL string
2577+
Plan []string
2578+
Warning []string
2579+
}
2580+
)
2581+
planSuiteData := core.GetPlanSuiteData()
2582+
planSuiteData.LoadTestCases(t, &input, &output)
2583+
for i, ts := range input {
2584+
testdata.OnRecord(func() {
2585+
output[i].SQL = ts
2586+
output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows())
2587+
})
2588+
tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...))
2589+
}
2590+
}

planner/core/testdata/plan_suite_in.json

+9
Original file line numberDiff line numberDiff line change
@@ -1148,5 +1148,14 @@
11481148
"select * from t where (a = 1 or b = 2) and c = 3 order by c limit 2",
11491149
"select * from t where (a = 1 or b = 2) and c in (1, 2, 3) order by c limit 2"
11501150
]
1151+
},
1152+
{
1153+
"name": "TestIssues49377Plan",
1154+
"cases": [
1155+
"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 );",
1156+
"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);",
1157+
"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);",
1158+
"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);"
1159+
]
11511160
}
11521161
]

planner/core/testdata/plan_suite_out.json

+98
Original file line numberDiff line numberDiff line change
@@ -6945,5 +6945,103 @@
69456945
"Warning": null
69466946
}
69476947
]
6948+
},
6949+
{
6950+
"Name": "TestIssues49377Plan",
6951+
"Cases": [
6952+
{
6953+
"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 );",
6954+
"Plan": [
6955+
"Union 21.00 root ",
6956+
"├─Projection 1.00 root 1->Column#15, 1->Column#16, 1->Column#17",
6957+
"│ └─TableDual 1.00 root rows:1",
6958+
"└─Projection 20.00 root cast(Column#12, bigint(11) BINARY)->Column#15, Column#13, cast(Column#14, bigint(11) BINARY)->Column#17",
6959+
" └─Sort 20.00 root Column#12",
6960+
" └─Union 20.00 root ",
6961+
" ├─TableReader 10.00 root data:Selection",
6962+
" │ └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
6963+
" │ └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo",
6964+
" └─Sort 10.00 root test.employee.employee_id",
6965+
" └─TableReader 10.00 root data:Selection",
6966+
" └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
6967+
" └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo"
6968+
],
6969+
"Warning": null
6970+
},
6971+
{
6972+
"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);",
6973+
"Plan": [
6974+
"Union 2.00 root ",
6975+
"├─Projection 1.00 root 1->Column#15, 1->Column#16, 1->Column#17",
6976+
"│ └─TableDual 1.00 root rows:1",
6977+
"└─Projection 1.00 root cast(Column#12, bigint(11) BINARY)->Column#15, Column#13, cast(Column#14, bigint(11) BINARY)->Column#17",
6978+
" └─TopN 1.00 root Column#12, offset:0, count:1",
6979+
" └─Union 2.00 root ",
6980+
" ├─TopN 1.00 root test.employee.employee_id, offset:0, count:1",
6981+
" │ └─TableReader 1.00 root data:TopN",
6982+
" │ └─TopN 1.00 cop[tikv] test.employee.employee_id, offset:0, count:1",
6983+
" │ └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
6984+
" │ └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo",
6985+
" └─TopN 1.00 root test.employee.employee_id, offset:0, count:1",
6986+
" └─TableReader 1.00 root data:TopN",
6987+
" └─TopN 1.00 cop[tikv] test.employee.employee_id, offset:0, count:1",
6988+
" └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
6989+
" └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo"
6990+
],
6991+
"Warning": null
6992+
},
6993+
{
6994+
"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);",
6995+
"Plan": [
6996+
"Union 21.00 root ",
6997+
"├─TableReader 10.00 root data:Selection",
6998+
"│ └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
6999+
"│ └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo",
7000+
"├─Sort 10.00 root test.employee.employee_id",
7001+
"│ └─TableReader 10.00 root data:Selection",
7002+
"│ └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
7003+
"│ └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo",
7004+
"└─Limit 1.00 root offset:0, count:1",
7005+
" └─Union 1.00 root ",
7006+
" ├─Limit 1.00 root offset:0, count:1",
7007+
" │ └─TableReader 1.00 root data:Limit",
7008+
" │ └─Limit 1.00 cop[tikv] offset:0, count:1",
7009+
" │ └─Selection 1.00 cop[tikv] eq(test.employee.dept_id, 1)",
7010+
" │ └─TableFullScan 1000.00 cop[tikv] table:employee keep order:false, stats:pseudo",
7011+
" └─TopN 1.00 root test.employee.employee_id, offset:0, count:1",
7012+
" └─TableReader 1.00 root data:TopN",
7013+
" └─TopN 1.00 cop[tikv] test.employee.employee_id, offset:0, count:1",
7014+
" └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
7015+
" └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo"
7016+
],
7017+
"Warning": null
7018+
},
7019+
{
7020+
"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);",
7021+
"Plan": [
7022+
"Union 21.00 root ",
7023+
"├─TableReader 10.00 root data:Selection",
7024+
"│ └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
7025+
"│ └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo",
7026+
"├─Sort 10.00 root test.employee.employee_id",
7027+
"│ └─TableReader 10.00 root data:Selection",
7028+
"│ └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
7029+
"│ └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo",
7030+
"└─TopN 1.00 root Column#17, offset:0, count:1",
7031+
" └─Union 2.00 root ",
7032+
" ├─TopN 1.00 root test.employee.employee_id, offset:0, count:1",
7033+
" │ └─TableReader 1.00 root data:TopN",
7034+
" │ └─TopN 1.00 cop[tikv] test.employee.employee_id, offset:0, count:1",
7035+
" │ └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
7036+
" │ └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo",
7037+
" └─TopN 1.00 root test.employee.employee_id, offset:0, count:1",
7038+
" └─TableReader 1.00 root data:TopN",
7039+
" └─TopN 1.00 cop[tikv] test.employee.employee_id, offset:0, count:1",
7040+
" └─Selection 10.00 cop[tikv] eq(test.employee.dept_id, 1)",
7041+
" └─TableFullScan 10000.00 cop[tikv] table:employee keep order:false, stats:pseudo"
7042+
],
7043+
"Warning": null
7044+
}
7045+
]
69487046
}
69497047
]

0 commit comments

Comments
 (0)