Skip to content

Commit

Permalink
planner/core: refine explain for window function (#9270)
Browse files Browse the repository at this point in the history
  • Loading branch information
alivxxx authored Feb 13, 2019
1 parent ba68605 commit 18ff4f9
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 16 deletions.
1 change: 1 addition & 0 deletions cmd/explaintest/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ func newTester(name string) *tester {
t.name = name
t.enableQueryLog = true
t.ctx = mock.NewContext()
t.ctx.GetSessionVars().EnableWindowFunction = true

return t
}
Expand Down
51 changes: 51 additions & 0 deletions cmd/explaintest/r/window_function.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use test;
drop table if exists t;
create table t (a int, b int, c timestamp, index idx(a));
set @@tidb_enable_window_function = 1;
explain select sum(a) over() from t;
id count task operator info
Projection_7 10000.00 root sum(a) over()
└─Window_8 10000.00 root sum(cast(test.t.a)) over()
└─TableReader_10 10000.00 root data:TableScan_9
└─TableScan_9 10000.00 cop table:t, range:[-inf,+inf], keep order:false, stats:pseudo
explain select sum(a) over(partition by a) from t;
id count task operator info
Projection_7 10000.00 root sum(a) over(partition by a)
└─Window_8 10000.00 root sum(cast(test.t.a)) over(partition by test.t.a)
└─IndexReader_11 10000.00 root index:IndexScan_10
└─IndexScan_10 10000.00 cop table:t, index:a, range:[NULL,+inf], keep order:true, stats:pseudo
explain select sum(a) over(partition by a order by b) from t;
id count task operator info
Projection_7 10000.00 root sum(a) over(partition by a order by b)
└─Window_8 10000.00 root sum(cast(test.t.a)) over(partition by test.t.a order by test.t.b asc)
└─Sort_14 10000.00 root test.t.a:asc, test.t.b:asc
└─TableReader_13 10000.00 root data:TableScan_12
└─TableScan_12 10000.00 cop table:t, range:[-inf,+inf], keep order:false, stats:pseudo
explain select sum(a) over(partition by a order by b rows unbounded preceding) from t;
id count task operator info
Projection_7 10000.00 root sum(a) over(partition by a order by b rows unbounded preceding)
└─Window_8 10000.00 root sum(cast(test.t.a)) over(partition by test.t.a order by test.t.b asc rows between unbounded preceding and current row)
└─Sort_14 10000.00 root test.t.a:asc, test.t.b:asc
└─TableReader_13 10000.00 root data:TableScan_12
└─TableScan_12 10000.00 cop table:t, range:[-inf,+inf], keep order:false, stats:pseudo
explain select sum(a) over(partition by a order by b rows between 1 preceding and 1 following) from t;
id count task operator info
Projection_7 10000.00 root sum(a) over(partition by a order by b rows between 1 preceding and 1 following)
└─Window_8 10000.00 root sum(cast(test.t.a)) over(partition by test.t.a order by test.t.b asc rows between 1 preceding and 1 following)
└─Sort_14 10000.00 root test.t.a:asc, test.t.b:asc
└─TableReader_13 10000.00 root data:TableScan_12
└─TableScan_12 10000.00 cop table:t, range:[-inf,+inf], keep order:false, stats:pseudo
explain select sum(a) over(partition by a order by b range between 1 preceding and 1 following) from t;
id count task operator info
Projection_7 10000.00 root sum(a) over(partition by a order by b range between 1 preceding and 1 following)
└─Window_8 10000.00 root sum(cast(test.t.a)) over(partition by test.t.a order by test.t.b asc range between 1 preceding and 1 following)
└─Sort_14 10000.00 root test.t.a:asc, test.t.b:asc
└─TableReader_13 10000.00 root data:TableScan_12
└─TableScan_12 10000.00 cop table:t, range:[-inf,+inf], keep order:false, stats:pseudo
explain select sum(a) over(partition by a order by c range between interval '2:30' minute_second preceding and interval '2:30' minute_second following) from t;
id count task operator info
Projection_7 10000.00 root sum(a) over(partition by a order by c range between interval '2:30' minute_second preceding and interval '2:30' minute_second following)
└─Window_8 10000.00 root sum(cast(test.t.a)) over(partition by test.t.a order by test.t.c asc range between interval "2:30" "MINUTE_SECOND" preceding and interval "2:30" "MINUTE_SECOND" following)
└─Sort_14 10000.00 root test.t.a:asc, test.t.c:asc
└─TableReader_13 10000.00 root data:TableScan_12
└─TableScan_12 10000.00 cop table:t, range:[-inf,+inf], keep order:false, stats:pseudo
11 changes: 11 additions & 0 deletions cmd/explaintest/t/window_function.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use test;
drop table if exists t;
create table t (a int, b int, c timestamp, index idx(a));
set @@tidb_enable_window_function = 1;
explain select sum(a) over() from t;
explain select sum(a) over(partition by a) from t;
explain select sum(a) over(partition by a order by b) from t;
explain select sum(a) over(partition by a order by b rows unbounded preceding) from t;
explain select sum(a) over(partition by a order by b rows between 1 preceding and 1 following) from t;
explain select sum(a) over(partition by a order by b range between 1 preceding and 1 following) from t;
explain select sum(a) over(partition by a order by c range between interval '2:30' minute_second preceding and interval '2:30' minute_second following) from t;
1 change: 1 addition & 0 deletions planner/core/exhaust_physical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,7 @@ func (p *LogicalWindow) exhaustPhysicalPlans(prop *property.PhysicalProperty) []
WindowFuncDesc: p.WindowFuncDesc,
PartitionBy: p.PartitionBy,
OrderBy: p.OrderBy,
Frame: p.Frame,
}.Init(p.ctx, p.stats.ScaleByExpectCnt(prop.ExpectedCnt), childProperty)
window.SetSchema(p.Schema())
return []PhysicalPlan{window}
Expand Down
70 changes: 68 additions & 2 deletions planner/core/explain.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"bytes"
"fmt"

"github.com/pingcap/parser/ast"
"github.com/pingcap/tidb/expression"
"github.com/pingcap/tidb/expression/aggregation"
)
Expand Down Expand Up @@ -298,8 +299,73 @@ func (p *PhysicalTopN) ExplainInfo() string {
return buffer.String()
}

func (p *PhysicalWindow) formatFrameBound(buffer *bytes.Buffer, bound *FrameBound) {
if bound.Type == ast.CurrentRow {
buffer.WriteString("current row")
return
}
if bound.UnBounded {
buffer.WriteString("unbounded")
} else if bound.DateCalcFunc != nil {
sf := bound.DateCalcFunc.(*expression.ScalarFunction)
// for `interval '2:30' minute_second`.
fmt.Fprintf(buffer, "interval %s %s", sf.GetArgs()[1].ExplainInfo(), sf.GetArgs()[2].ExplainInfo())
} else {
fmt.Fprintf(buffer, "%d", bound.Num)
}
if bound.Type == ast.Preceding {
buffer.WriteString(" preceding")
} else {
buffer.WriteString(" following")
}
}

// ExplainInfo implements PhysicalPlan interface.
func (p *PhysicalWindow) ExplainInfo() string {
// TODO: Add explain info for partition by, order by and frame.
return p.WindowFuncDesc.String()
buffer := bytes.NewBufferString(p.WindowFuncDesc.String())
buffer.WriteString(" over(")
isFirst := true
if len(p.PartitionBy) > 0 {
buffer.WriteString("partition by ")
for i, item := range p.PartitionBy {
fmt.Fprintf(buffer, "%s", item.Col.ExplainInfo())
if i+1 < len(p.PartitionBy) {
buffer.WriteString(", ")
}
}
isFirst = false
}
if len(p.OrderBy) > 0 {
if !isFirst {
buffer.WriteString(" ")
}
buffer.WriteString("order by ")
for i, item := range p.OrderBy {
order := "asc"
if item.Desc {
order = "desc"
}
fmt.Fprintf(buffer, "%s %s", item.Col.ExplainInfo(), order)
if i+1 < len(p.OrderBy) {
buffer.WriteString(", ")
}
}
isFirst = false
}
if p.Frame != nil {
if !isFirst {
buffer.WriteString(" ")
}
if p.Frame.Type == ast.Rows {
buffer.WriteString("rows")
} else {
buffer.WriteString("range")
}
buffer.WriteString(" between ")
p.formatFrameBound(buffer, p.Frame.Start)
buffer.WriteString(" and ")
p.formatFrameBound(buffer, p.Frame.End)
}
buffer.WriteString(")")
return buffer.String()
}
26 changes: 13 additions & 13 deletions planner/core/logical_plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2051,51 +2051,51 @@ func (s *testPlanSuite) TestWindowFunction(c *C) {
}{
{
sql: "select a, avg(a) over(partition by a) from t",
result: "TableReader(Table(t))->Window(avg(cast(test.t.a)))->Projection",
result: "TableReader(Table(t))->Window(avg(cast(test.t.a)) over(partition by test.t.a))->Projection",
},
{
sql: "select a, avg(a) over(partition by b) from t",
result: "TableReader(Table(t))->Sort->Window(avg(cast(test.t.a)))->Projection",
result: "TableReader(Table(t))->Sort->Window(avg(cast(test.t.a)) over(partition by test.t.b))->Projection",
},
{
sql: "select a, avg(a+1) over(partition by (a+1)) from t",
result: "TableReader(Table(t))->Projection->Sort->Window(avg(cast(2_proj_window_3)))->Projection",
result: "TableReader(Table(t))->Projection->Sort->Window(avg(cast(2_proj_window_3)) over(partition by 2_proj_window_2))->Projection",
},
{
sql: "select a, avg(a) over(order by a asc, b desc) from t order by a asc, b desc",
result: "TableReader(Table(t))->Sort->Window(avg(cast(test.t.a)))->Projection",
result: "TableReader(Table(t))->Sort->Window(avg(cast(test.t.a)) over(order by test.t.a asc, test.t.b desc))->Projection",
},
{
sql: "select a, b as a, avg(a) over(partition by a) from t",
result: "TableReader(Table(t))->Window(avg(cast(test.t.a)))->Projection",
result: "TableReader(Table(t))->Window(avg(cast(test.t.a)) over(partition by test.t.a))->Projection",
},
{
sql: "select a, b as z, sum(z) over() from t",
result: "[planner:1054]Unknown column 'z' in 'field list'",
},
{
sql: "select a, b as z from t order by (sum(z) over())",
result: "TableReader(Table(t))->Window(sum(cast(test.t.z)))->Sort->Projection",
result: "TableReader(Table(t))->Window(sum(cast(test.t.z)) over())->Sort->Projection",
},
{
sql: "select sum(avg(a)) over() from t",
result: "TableReader(Table(t)->StreamAgg)->StreamAgg->Window(sum(sel_agg_2))->Projection",
result: "TableReader(Table(t)->StreamAgg)->StreamAgg->Window(sum(sel_agg_2) over())->Projection",
},
{
sql: "select b from t order by(sum(a) over())",
result: "TableReader(Table(t))->Window(sum(cast(test.t.a)))->Sort->Projection",
result: "TableReader(Table(t))->Window(sum(cast(test.t.a)) over())->Sort->Projection",
},
{
sql: "select b from t order by(sum(a) over(partition by a))",
result: "TableReader(Table(t))->Window(sum(cast(test.t.a)))->Sort->Projection",
result: "TableReader(Table(t))->Window(sum(cast(test.t.a)) over(partition by test.t.a))->Sort->Projection",
},
{
sql: "select b from t order by(sum(avg(a)) over())",
result: "TableReader(Table(t)->StreamAgg)->StreamAgg->Window(sum(sel_agg_2))->Sort->Projection",
result: "TableReader(Table(t)->StreamAgg)->StreamAgg->Window(sum(sel_agg_2) over())->Sort->Projection",
},
{
sql: "select a from t having (select sum(a) over() as w from t tt where a > t.a)",
result: "Apply{TableReader(Table(t))->TableReader(Table(t)->Sel([gt(tt.a, test.t.a)]))->Window(sum(cast(tt.a)))->MaxOneRow->Sel([w])}->Projection",
result: "Apply{TableReader(Table(t))->TableReader(Table(t)->Sel([gt(tt.a, test.t.a)]))->Window(sum(cast(tt.a)) over())->MaxOneRow->Sel([w])}->Projection",
},
{
sql: "select avg(a) over() as w from t having w > 1",
Expand Down Expand Up @@ -2135,11 +2135,11 @@ func (s *testPlanSuite) TestWindowFunction(c *C) {
},
{
sql: "select sum(a) over(w1), avg(a) over(w2) from t window w1 as (partition by a), w2 as (w1)",
result: "TableReader(Table(t))->Window(sum(cast(test.t.a)))->Window(avg(cast(test.t.a)))->Projection",
result: "TableReader(Table(t))->Window(sum(cast(test.t.a)) over(partition by test.t.a))->Window(avg(cast(test.t.a)) over())->Projection",
},
{
sql: "select a from t window w1 as (partition by a) order by (sum(a) over(w1))",
result: "TableReader(Table(t))->Window(sum(cast(test.t.a)))->Sort->Projection",
result: "TableReader(Table(t))->Window(sum(cast(test.t.a)) over(partition by test.t.a))->Sort->Projection",
},
{
sql: "select sum(a) over(groups 1 preceding) from t",
Expand Down
1 change: 1 addition & 0 deletions planner/core/physical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,4 +383,5 @@ type PhysicalWindow struct {
WindowFuncDesc *aggregation.WindowFuncDesc
PartitionBy []property.Item
OrderBy []property.Item
Frame *WindowFrame
}
2 changes: 1 addition & 1 deletion planner/core/stringer.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ func toString(in Plan, strs []string, idxs []int) ([]string, []int) {
case *LogicalWindow:
str = fmt.Sprintf("Window(%s)", x.WindowFuncDesc.String())
case *PhysicalWindow:
str = fmt.Sprintf("Window(%s)", x.WindowFuncDesc.String())
str = fmt.Sprintf("Window(%s)", x.ExplainInfo())
default:
str = fmt.Sprintf("%T", in)
}
Expand Down

0 comments on commit 18ff4f9

Please sign in to comment.