From 1aaf51340fd9ae9b7b619b49fff51b62d6079db5 Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Fri, 7 Dec 2018 17:04:39 +0800 Subject: [PATCH 01/17] planner: support window function --- expression/aggregation/window_func.go | 29 ++++ go.mod | 5 +- go.sum | 6 + planner/core/exhaust_physical_plans.go | 12 ++ planner/core/explain.go | 5 + planner/core/expression_rewriter.go | 15 ++- planner/core/initialize.go | 16 +++ planner/core/logical_plan_builder.go | 175 +++++++++++++++++++++++-- planner/core/logical_plan_test.go | 78 +++++++++++ planner/core/logical_plans.go | 16 +++ planner/core/physical_plans.go | 9 ++ planner/core/planbuilder.go | 11 ++ planner/core/rule_column_pruning.go | 31 ++++- planner/core/stats.go | 18 +++ planner/core/stringer.go | 4 + 15 files changed, 418 insertions(+), 12 deletions(-) create mode 100644 expression/aggregation/window_func.go diff --git a/expression/aggregation/window_func.go b/expression/aggregation/window_func.go new file mode 100644 index 0000000000000..9555f900bdb73 --- /dev/null +++ b/expression/aggregation/window_func.go @@ -0,0 +1,29 @@ +// Copyright 2018 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package aggregation + +import ( + "github.com/pingcap/tidb/expression" + "github.com/pingcap/tidb/sessionctx" +) + +// WindowFuncDesc describes a window function signature, only used in planner. +type WindowFuncDesc struct { + baseFuncDesc +} + +// NewWindowFuncDesc creates a window function signature descriptor. +func NewWindowFuncDesc(ctx sessionctx.Context, name string, args []expression.Expression) *WindowFuncDesc { + return &WindowFuncDesc{newBaseFuncDesc(ctx, name, args)} +} diff --git a/go.mod b/go.mod index d3519148c6ed1..34bde6c521802 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway v1.5.1 // indirect github.com/jonboulle/clockwork v0.1.0 // indirect github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5 + github.com/lamxTyler/parser v0.0.0-20181105132130-fc47682b601a // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/montanaflynn/stats v0.0.0-20180911141734-db72e6cae808 // indirect github.com/myesui/uuid v1.0.0 // indirect @@ -47,7 +48,7 @@ require ( github.com/pingcap/errors v0.11.0 github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e github.com/pingcap/kvproto v0.0.0-20181105061835-1b5d69cd1d26 - github.com/pingcap/parser v0.0.0-20181204031237-721bcaad1aeb + github.com/pingcap/parser v0.0.0-20181205023950-c563561800a2 github.com/pingcap/pd v2.1.0-rc.4+incompatible github.com/pingcap/tidb-tools v0.0.0-20181112132202-4860a0d5de03 github.com/pingcap/tipb v0.0.0-20181012112600-11e33c750323 @@ -85,3 +86,5 @@ require ( sourcegraph.com/sourcegraph/appdash v0.0.0-20180531100431-4c381bd170b4 sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67 ) + +replace github.com/pingcap/parser => github.com/lamxTyler/parser v0.0.0-20181206094044-9068edb453e8 diff --git a/go.sum b/go.sum index df2fac24da5f8..aedbfa799e8aa 100644 --- a/go.sum +++ b/go.sum @@ -79,6 +79,12 @@ github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5 h1:2U0HzY8BJ8hVwDK github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/lamxTyler/parser v0.0.0-20181105132130-fc47682b601a h1:LNzXeT9i0ATF41gbmDKa+PVvsrKgpcNRGBTxUK8d1BE= +github.com/lamxTyler/parser v0.0.0-20181105132130-fc47682b601a/go.mod h1:6c1rwSy9dUuNebYdr1IMI4+/sT3/Q65MXP2UCg7/vJI= +github.com/lamxTyler/parser v0.0.0-20181128083547-163b62bbc729 h1:z/5Im9MtTtuRGEiVLymiXUvW8IeNJ6PCo4v4gVieAgI= +github.com/lamxTyler/parser v0.0.0-20181128083547-163b62bbc729/go.mod h1:6c1rwSy9dUuNebYdr1IMI4+/sT3/Q65MXP2UCg7/vJI= +github.com/lamxTyler/parser v0.0.0-20181206094044-9068edb453e8 h1:8OgL0B1Q0ado0gQqSTgiL3JKH7IgNacKbXc3sglwtY4= +github.com/lamxTyler/parser v0.0.0-20181206094044-9068edb453e8/go.mod h1:6c1rwSy9dUuNebYdr1IMI4+/sT3/Q65MXP2UCg7/vJI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/montanaflynn/stats v0.0.0-20180911141734-db72e6cae808 h1:pmpDGKLw4n82EtrNiLqB+xSz/JQwFOaZuMALYUHwX5s= diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index 67afd825f923b..4efe7fb1747a4 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -779,6 +779,18 @@ func (la *LogicalApply) exhaustPhysicalPlans(prop *property.PhysicalProperty) [] return []PhysicalPlan{apply} } +func (p *LogicalWindowFunc) exhaustPhysicalPlans(prop *property.PhysicalProperty) []PhysicalPlan { + if !matchItems(prop, p.ByItems) { + return nil + } + window := PhysicalWindowFunc{ + Desc: p.Desc, + PartitionBy: p.PartitionBy, + }.Init(p.ctx, p.stats.ScaleByExpectCnt(prop.ExpectedCnt), &property.PhysicalProperty{ExpectedCnt: math.MaxFloat64}) + window.SetSchema(p.Schema()) + return []PhysicalPlan{window} +} + // exhaustPhysicalPlans is only for implementing interface. DataSource and Dual generate task in `findBestTask` directly. func (p *baseLogicalPlan) exhaustPhysicalPlans(_ *property.PhysicalProperty) []PhysicalPlan { panic("baseLogicalPlan.exhaustPhysicalPlans() should never be called.") diff --git a/planner/core/explain.go b/planner/core/explain.go index f4b37efd869d1..61ace9512db49 100644 --- a/planner/core/explain.go +++ b/planner/core/explain.go @@ -297,3 +297,8 @@ func (p *PhysicalTopN) ExplainInfo() string { fmt.Fprintf(buffer, ", offset:%v, count:%v", p.Offset, p.Count) return buffer.String() } + +// ExplainInfo implements PhysicalPlan interface. +func (p *PhysicalWindowFunc) ExplainInfo() string { + return p.Desc.String() +} diff --git a/planner/core/expression_rewriter.go b/planner/core/expression_rewriter.go index dec7ba06af6cf..68a426ab51551 100644 --- a/planner/core/expression_rewriter.go +++ b/planner/core/expression_rewriter.go @@ -303,12 +303,25 @@ func (er *expressionRewriter) Enter(inNode ast.Node) (ast.Node, bool) { } er.ctxStack = append(er.ctxStack, expression.NewValuesFunc(er.ctx, col.Index, col.RetType)) return inNode, true + case *ast.WindowFuncExpr: + return er.handleWindowFunction(v) default: er.asScalar = true } return inNode, false } +func (er *expressionRewriter) handleWindowFunction(v *ast.WindowFuncExpr) (ast.Node, bool) { + windowPlan, err := er.b.buildWindowFunction(er.p, v, er.aggrMap) + if err != nil { + er.err = errors.Trace(err) + return v, false + } + er.ctxStack = append(er.ctxStack, windowPlan.GetWindowResultColumn()) + er.p = windowPlan + return v, true +} + func (er *expressionRewriter) handleCompareSubquery(v *ast.CompareSubqueryExpr) (ast.Node, bool) { v.L.Accept(er) if er.err != nil { @@ -750,7 +763,7 @@ func (er *expressionRewriter) Leave(originInNode ast.Node) (retNode ast.Node, ok } switch v := inNode.(type) { case *ast.AggregateFuncExpr, *ast.ColumnNameExpr, *ast.ParenthesesExpr, *ast.WhenClause, - *ast.SubqueryExpr, *ast.ExistsSubqueryExpr, *ast.CompareSubqueryExpr, *ast.ValuesExpr: + *ast.SubqueryExpr, *ast.ExistsSubqueryExpr, *ast.CompareSubqueryExpr, *ast.ValuesExpr, *ast.WindowFuncExpr: case *driver.ValueExpr: value := &expression.Constant{Value: v.Datum, RetType: &v.Type} er.ctxStack = append(er.ctxStack, value) diff --git a/planner/core/initialize.go b/planner/core/initialize.go index 9c2b698c927f0..8b76ea8153ff6 100644 --- a/planner/core/initialize.go +++ b/planner/core/initialize.go @@ -81,6 +81,8 @@ const ( TypeTableReader = "TableReader" // TypeIndexReader is the type of IndexReader. TypeIndexReader = "IndexReader" + // TypeWindowFunc is the type of WindowFunc. + TypeWindowFunc = "WindowFunc" ) // Init initializes LogicalAggregation. @@ -231,6 +233,20 @@ func (p PhysicalMaxOneRow) Init(ctx sessionctx.Context, stats *property.StatsInf return &p } +// Init initializes LogicalWindowFunc. +func (p LogicalWindowFunc) Init(ctx sessionctx.Context) *LogicalWindowFunc { + p.baseLogicalPlan = newBaseLogicalPlan(ctx, TypeWindowFunc, &p) + return &p +} + +// Init initializes PhysicalWindowFunc. +func (p PhysicalWindowFunc) Init(ctx sessionctx.Context, stats *property.StatsInfo, props ...*property.PhysicalProperty) *PhysicalWindowFunc { + p.basePhysicalPlan = newBasePhysicalPlan(ctx, TypeWindowFunc, &p) + p.childrenReqProps = props + p.stats = stats + return &p +} + // Init initializes Update. func (p Update) Init(ctx sessionctx.Context) *Update { p.basePlan = newBasePlan(ctx, TypeUpdate) diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index 8179ee33fbf87..95d03a4e649c9 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -602,6 +602,14 @@ func (b *PlanBuilder) buildProjection(p LogicalPlan, fields []*ast.SelectField, schema := expression.NewSchema(make([]*expression.Column, 0, len(fields))...) oldLen := 0 for _, field := range fields { + // Add fake placeholders. + if ast.HasWindowFlag(field.Expr) { + expr := expression.Zero + proj.Exprs = append(proj.Exprs, expr) + col := b.buildProjectionField(proj.id, schema.Len()+1, field, expr) + schema.Append(col) + continue + } newExpr, np, err := b.rewrite(field.Expr, p, mapper, true) if err != nil { return nil, 0, errors.Trace(err) @@ -998,10 +1006,11 @@ func resolveFromSelectFields(v *ast.ColumnNameExpr, fields []*ast.SelectField, i return } -// havingAndOrderbyExprResolver visits Expr tree. +// havingWindowAndOrderbyExprResolver visits Expr tree. // It converts ColunmNameExpr to AggregateFuncExpr and collects AggregateFuncExpr. -type havingAndOrderbyExprResolver struct { +type havingWindowAndOrderbyExprResolver struct { inAggFunc bool + inWindowFunc bool inExpr bool orderBy bool err error @@ -1015,10 +1024,12 @@ type havingAndOrderbyExprResolver struct { } // Enter implements Visitor interface. -func (a *havingAndOrderbyExprResolver) Enter(n ast.Node) (node ast.Node, skipChildren bool) { +func (a *havingWindowAndOrderbyExprResolver) Enter(n ast.Node) (node ast.Node, skipChildren bool) { switch n.(type) { case *ast.AggregateFuncExpr: a.inAggFunc = true + case *ast.WindowFuncExpr: + a.inWindowFunc = true case *driver.ParamMarkerExpr, *ast.ColumnNameExpr, *ast.ColumnName: case *ast.SubqueryExpr, *ast.ExistsSubqueryExpr: // Enter a new context, skip it. @@ -1030,7 +1041,7 @@ func (a *havingAndOrderbyExprResolver) Enter(n ast.Node) (node ast.Node, skipChi return n, false } -func (a *havingAndOrderbyExprResolver) resolveFromSchema(v *ast.ColumnNameExpr, schema *expression.Schema) (int, error) { +func (a *havingWindowAndOrderbyExprResolver) resolveFromSchema(v *ast.ColumnNameExpr, schema *expression.Schema) (int, error) { col, err := schema.FindColumn(v.Name) if err != nil { return -1, errors.Trace(err) @@ -1044,7 +1055,7 @@ func (a *havingAndOrderbyExprResolver) resolveFromSchema(v *ast.ColumnNameExpr, Name: col.ColName, } for i, field := range a.selectFields { - if c, ok := field.Expr.(*ast.ColumnNameExpr); ok && colMatch(newColName, c.Name) { + if c, ok := field.Expr.(*ast.ColumnNameExpr); ok && colMatch(c.Name, newColName) { return i, nil } } @@ -1058,7 +1069,7 @@ func (a *havingAndOrderbyExprResolver) resolveFromSchema(v *ast.ColumnNameExpr, } // Leave implements Visitor interface. -func (a *havingAndOrderbyExprResolver) Leave(n ast.Node) (node ast.Node, ok bool) { +func (a *havingWindowAndOrderbyExprResolver) Leave(n ast.Node) (node ast.Node, ok bool) { switch v := n.(type) { case *ast.AggregateFuncExpr: a.inAggFunc = false @@ -1068,9 +1079,15 @@ func (a *havingAndOrderbyExprResolver) Leave(n ast.Node) (node ast.Node, ok bool Expr: v, AsName: model.NewCIStr(fmt.Sprintf("sel_agg_%d", len(a.selectFields))), }) + case *ast.WindowFuncExpr: + a.inWindowFunc = false + if a.curClause == havingClause { + a.err = errors.Errorf("You cannot use the window function '%s' in this context.", v.F) + return node, false + } case *ast.ColumnNameExpr: resolveFieldsFirst := true - if a.inAggFunc || (a.orderBy && a.inExpr) { + if a.inAggFunc || a.inWindowFunc || (a.orderBy && a.inExpr) { resolveFieldsFirst = false } if !a.inAggFunc && !a.orderBy { @@ -1088,6 +1105,10 @@ func (a *havingAndOrderbyExprResolver) Leave(n ast.Node) (node ast.Node, ok bool if a.err != nil { return node, false } + if index != -1 && a.curClause == havingClause && ast.HasWindowFlag(a.selectFields[index].Expr) { + a.err = errors.Errorf("You cannot use the alias '%s' of an expression containing a window function in this context.", v.Name.Name.O) + return node, false + } if index == -1 { if a.orderBy { index, a.err = a.resolveFromSchema(v, a.p.Schema()) @@ -1101,8 +1122,12 @@ func (a *havingAndOrderbyExprResolver) Leave(n ast.Node) (node ast.Node, ok bool var err error index, err = a.resolveFromSchema(v, a.p.Schema()) _ = err - if index == -1 { + if index == -1 && a.curClause != windowClause { index, a.err = resolveFromSelectFields(v, a.selectFields, false) + if index != -1 && a.curClause == havingClause && ast.HasWindowFlag(a.selectFields[index].Expr) { + a.err = errors.Errorf("You cannot use the alias '%s' of an expression containing a window function in this context.", v.Name.Name.O) + return node, false + } } } if a.err != nil { @@ -1136,7 +1161,7 @@ func (a *havingAndOrderbyExprResolver) Leave(n ast.Node) (node ast.Node, ok bool // When we rewrite the order by / having expression, we will find column in map at first. func (b *PlanBuilder) resolveHavingAndOrderBy(sel *ast.SelectStmt, p LogicalPlan) ( map[*ast.AggregateFuncExpr]int, map[*ast.AggregateFuncExpr]int, error) { - extractor := &havingAndOrderbyExprResolver{ + extractor := &havingWindowAndOrderbyExprResolver{ p: p, selectFields: sel.Fields.Fields, aggMapper: make(map[*ast.AggregateFuncExpr]int), @@ -1189,6 +1214,31 @@ func (b *PlanBuilder) extractAggFuncs(fields []*ast.SelectField) ([]*ast.Aggrega return aggList, totalAggMapper } +// resolveWindowFunction will process window functions and resolve the columns that don't exist in select fields. +func (b *PlanBuilder) resolveWindowFunction(sel *ast.SelectStmt, p LogicalPlan) ( + map[*ast.AggregateFuncExpr]int, error) { + extractor := &havingWindowAndOrderbyExprResolver{ + p: p, + selectFields: sel.Fields.Fields, + aggMapper: make(map[*ast.AggregateFuncExpr]int), + colMapper: b.colMapper, + outerSchemas: b.outerSchemas, + } + extractor.curClause = windowClause + for _, field := range sel.Fields.Fields { + if !ast.HasWindowFlag(field.Expr) { + continue + } + n, ok := field.Expr.Accept(extractor) + if !ok { + return nil, errors.Trace(extractor.err) + } + field.Expr = n.(ast.ExprNode) + } + sel.Fields.Fields = extractor.selectFields + return extractor.aggMapper, nil +} + // gbyResolver resolves group by items from select fields. type gbyResolver struct { ctx sessionctx.Context @@ -1233,6 +1283,8 @@ func (g *gbyResolver) Leave(inNode ast.Node) (ast.Node, bool) { ret.Accept(extractor) if len(extractor.AggFuncs) != 0 { err = ErrIllegalReference.GenWithStackByArgs(v.Name.OrigColName(), "reference to group function") + } else if ast.HasWindowFlag(ret) { + err = ErrIllegalReference.GenWithStackByArgs(v.Name.OrigColName(), "reference to window function") } else { return ret, true } @@ -1726,6 +1778,7 @@ func (b *PlanBuilder) buildSelect(sel *ast.SelectStmt) (p LogicalPlan, err error var ( aggFuncs []*ast.AggregateFuncExpr havingMap, orderMap, totalMap map[*ast.AggregateFuncExpr]int + windowMap map[*ast.AggregateFuncExpr]int gbyCols []expression.Expression ) @@ -1758,6 +1811,13 @@ func (b *PlanBuilder) buildSelect(sel *ast.SelectStmt) (p LogicalPlan, err error } } + hasWindow := b.detectSelectWindow(sel) + if hasWindow { + windowMap, err = b.resolveWindowFunction(sel, p) + if err != nil { + return nil, errors.Trace(err) + } + } // We must resolve having and order by clause before build projection, // because when the query is "select a+1 as b from t having sum(b) < 0", we must replace sum(b) to sum(a+1), // which only can be done before building projection and extracting Agg functions. @@ -1804,6 +1864,13 @@ func (b *PlanBuilder) buildSelect(sel *ast.SelectStmt) (p LogicalPlan, err error } } + if hasWindow { + p, oldLen, err = b.buildWindowFunctions(p, sel, windowMap) + if err != nil { + return nil, errors.Trace(err) + } + } + if sel.Distinct { p = b.buildDistinct(p, oldLen) } @@ -2415,6 +2482,96 @@ func (b *PlanBuilder) buildDelete(delete *ast.DeleteStmt) (Plan, error) { return del, nil } +func (b *PlanBuilder) buildWindowFunctions(p LogicalPlan, sel *ast.SelectStmt, windowMap map[*ast.AggregateFuncExpr]int) (LogicalPlan, int, error) { + b.optFlag |= flagEliminateProjection + b.curClause = fieldList + fields := sel.Fields.Fields + proj := LogicalProjection{Exprs: make([]expression.Expression, 0, len(fields))}.Init(b.ctx) + schema := expression.NewSchema(make([]*expression.Column, 0, len(fields))...) + oldLen := 0 + for i, field := range fields { + if !field.Auxiliary { + oldLen++ + } + // it has been built in previous projection + if !ast.HasWindowFlag(field.Expr) { + col := p.Schema().Columns[i] + proj.Exprs = append(proj.Exprs, col) + newCol := b.buildProjectionField(proj.id, schema.Len()+1, field, col) + schema.Append(newCol) + continue + } + newExpr, np, err := b.rewrite(field.Expr, p, windowMap, true) + if err != nil { + return nil, 0, errors.Trace(err) + } + + p = np + proj.Exprs = append(proj.Exprs, newExpr) + + col := b.buildProjectionField(proj.id, schema.Len()+1, field, newExpr) + schema.Append(col) + } + proj.SetSchema(schema) + proj.SetChildren(p) + return proj, oldLen, nil +} + +func (b *PlanBuilder) buildWindowFunction(p LogicalPlan, expr *ast.WindowFuncExpr, aggMap map[*ast.AggregateFuncExpr]int) (*LogicalWindowFunc, error) { + spec := expr.Spec + // Add sort for partition by and order by + var byItems []*ast.ByItem + if spec.PartitionBy != nil { + byItems = append(byItems, spec.PartitionBy.Items...) + } + if spec.OrderBy != nil { + byItems = append(byItems, spec.OrderBy.Items...) + } + var partitionBy []expression.Expression + var orderByItems []*ByItems + if len(byItems) > 0 { + s, err := b.buildSort(p, byItems, nil) + if err != nil { + return nil, err + } + if spec.PartitionBy != nil { + partitionBy = make([]expression.Expression, 0, len(spec.PartitionBy.Items)) + for i := 0; i < len(spec.PartitionBy.Items); i++ { + partitionBy = append(partitionBy, s.ByItems[i].Expr) + } + } + orderByItems = s.ByItems + p = s + } + + newArgList := make([]expression.Expression, 0, len(expr.Args)) + for _, arg := range expr.Args { + newArg, np, err := b.rewrite(arg, p, aggMap, true) + if err != nil { + return nil, err + } + p = np + newArgList = append(newArgList, newArg) + } + + desc := aggregation.NewWindowFuncDesc(b.ctx, expr.F, newArgList) + window := LogicalWindowFunc{ + Desc: desc, + PartitionBy: partitionBy, + ByItems: orderByItems, + }.Init(b.ctx) + schema := p.Schema().Clone() + schema.Append(&expression.Column{ + ColName: model.NewCIStr(fmt.Sprintf("%d_window_%d", window.id, p.Schema().Len())), + UniqueID: b.ctx.GetSessionVars().AllocPlanColumnID(), + IsReferenced: true, + RetType: desc.RetTp, + }) + window.SetChildren(p) + window.SetSchema(schema) + return window, nil +} + // extractTableList extracts all the TableNames from node. func extractTableList(node ast.ResultSetNode, input []*ast.TableName) []*ast.TableName { switch x := node.(type) { diff --git a/planner/core/logical_plan_test.go b/planner/core/logical_plan_test.go index 10b5a25467798..d583531821f17 100644 --- a/planner/core/logical_plan_test.go +++ b/planner/core/logical_plan_test.go @@ -1852,3 +1852,81 @@ func (s *testPlanSuite) TestOuterJoinEliminator(c *C) { c.Assert(ToString(p), Equals, tt.best, comment) } } + +func (s *testPlanSuite) TestWindowFunction(c *C) { + defer testleak.AfterTest(c)() + tests := []struct { + sql string + result string + }{ + { + sql: "select a, avg(a) over() from t", + result: "DataScan(t)->WindowFunc(avg(test.t.a))->Projection", + }, + { + sql: "select a, avg(a) over(partition by a) from t", + result: "DataScan(t)->Sort->WindowFunc(avg(test.t.a))->Projection", + }, + { + sql: "select a, b as a, avg(a) over(partition by a) from t", + result: "DataScan(t)->Sort->WindowFunc(avg(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: "DataScan(t)->WindowFunc(sum(test.t.z))->Sort->Projection", + }, + { + sql: "select sum(avg(a)) over() from t", + result: "DataScan(t)->Aggr(avg(test.t.a))->WindowFunc(sum(sel_agg_2))->Projection", + }, + { + sql: "select b from t order by(sum(a) over())", + result: "DataScan(t)->WindowFunc(sum(test.t.a))->Sort->Projection", + }, + { + sql: "select b from t order by(sum(a) over(partition by a))", + result: "DataScan(t)->Sort->WindowFunc(sum(test.t.a))->Sort->Projection", + }, + { + sql: "select b from t order by(sum(avg(a)) over())", + result: "DataScan(t)->Aggr(avg(test.t.a),firstrow(test.t.b))->WindowFunc(sum(sel_agg_2))->Sort->Projection", + }, + { + sql: "select a from t having (select sum(a) over() as w from t tt where a > t.a)", + result: "Apply{DataScan(t)->DataScan(tt)->WindowFunc(sum(tt.a))->MaxOneRow->Sel([w])}->Projection", + }, + { + sql: "select avg(a) over() as w from t having w > 1", + result: "You cannot use the alias 'w' of an expression containing a window function in this context.", + }, + } + + s.Parser.EnableWindowFunc(true) + defer func() { + s.Parser.EnableWindowFunc(false) + }() + for i, tt := range tests { + comment := Commentf("case:%v sql:%s", i, tt.sql) + stmt, err := s.ParseOneStmt(tt.sql, "", "") + c.Assert(err, IsNil, comment) + Preprocess(s.ctx, stmt, s.is, false) + builder := &PlanBuilder{ + ctx: MockContext(), + is: s.is, + colMapper: make(map[*ast.ColumnNameExpr]int), + } + p, err := builder.Build(stmt) + if err != nil { + c.Assert(err.Error(), Equals, tt.result, comment) + continue + } + c.Assert(err, IsNil) + p, err = logicalOptimize(builder.optFlag, p.(LogicalPlan)) + c.Assert(err, IsNil) + c.Assert(ToString(p), Equals, tt.result, comment) + } +} diff --git a/planner/core/logical_plans.go b/planner/core/logical_plans.go index aa2cc510b67c9..1c25dffbc0910 100644 --- a/planner/core/logical_plans.go +++ b/planner/core/logical_plans.go @@ -42,6 +42,7 @@ var ( _ LogicalPlan = &LogicalSort{} _ LogicalPlan = &LogicalLock{} _ LogicalPlan = &LogicalLimit{} + _ LogicalPlan = &LogicalWindowFunc{} ) // JoinType contains CrossJoin, InnerJoin, LeftOuterJoin, RightOuterJoin, FullOuterJoin, SemiJoin. @@ -617,3 +618,18 @@ type LogicalLock struct { Lock ast.SelectLockType } + +// LogicalWindowFunc represents a logical window function plan. +type LogicalWindowFunc struct { + logicalSchemaProducer + + Desc *aggregation.WindowFuncDesc + ByItems []*ByItems + PartitionBy []expression.Expression + // TODO: add frame clause +} + +// GetWindowResultColumn returns the column storing the result of the window function. +func (p *LogicalWindowFunc) GetWindowResultColumn() *expression.Column { + return p.schema.Columns[p.schema.Len()-1] +} diff --git a/planner/core/physical_plans.go b/planner/core/physical_plans.go index 4c66330fabdfe..f0501e385a214 100644 --- a/planner/core/physical_plans.go +++ b/planner/core/physical_plans.go @@ -47,6 +47,7 @@ var ( _ PhysicalPlan = &PhysicalHashJoin{} _ PhysicalPlan = &PhysicalMergeJoin{} _ PhysicalPlan = &PhysicalUnionScan{} + _ PhysicalPlan = &PhysicalWindowFunc{} ) // PhysicalTableReader is the table reader in tidb. @@ -373,3 +374,11 @@ type PhysicalTableDual struct { RowCount int } + +// PhysicalWindowFunc is the physical operator of window function. +type PhysicalWindowFunc struct { + physicalSchemaProducer + + Desc *aggregation.WindowFuncDesc + PartitionBy []expression.Expression +} diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index cbf08c31c30a4..1070b553bd42a 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -92,6 +92,7 @@ const ( onClause orderByClause whereClause + windowClause groupByClause showStatement globalOrderByClause @@ -107,6 +108,7 @@ var clauseMsg = map[clauseCode]string{ groupByClause: "group statement", showStatement: "show statement", globalOrderByClause: "global ORDER clause", + windowClause: "field list", // For window functions that in field list. } // PlanBuilder builds Plan from an ast.Node. @@ -299,6 +301,15 @@ func (b *PlanBuilder) detectSelectAgg(sel *ast.SelectStmt) bool { return false } +func (b *PlanBuilder) detectSelectWindow(sel *ast.SelectStmt) bool { + for _, f := range sel.Fields.Fields { + if ast.HasWindowFlag(f.Expr) { + return true + } + } + return false +} + func getPathByIndexName(paths []*accessPath, idxName model.CIStr, tblInfo *model.TableInfo) *accessPath { var tablePath *accessPath for _, path := range paths { diff --git a/planner/core/rule_column_pruning.go b/planner/core/rule_column_pruning.go index 05b239ee3baf7..eee8a9cf3555f 100644 --- a/planner/core/rule_column_pruning.go +++ b/planner/core/rule_column_pruning.go @@ -15,7 +15,6 @@ package core import ( "fmt" - "github.com/pingcap/parser/ast" "github.com/pingcap/parser/model" "github.com/pingcap/tidb/expression" @@ -265,3 +264,33 @@ func (p *LogicalLock) PruneColumns(parentUsedCols []*expression.Column) { p.children[0].PruneColumns(parentUsedCols) } } + +// PruneColumns implements LogicalPlan interface. +func (p *LogicalWindowFunc) PruneColumns(parentUsedCols []*expression.Column) { + windowColumn := p.GetWindowResultColumn() + len := 0 + for _, col := range parentUsedCols { + if !windowColumn.Equal(nil, col) { + parentUsedCols[len] = col + len++ + } + } + parentUsedCols = parentUsedCols[:len] + parentUsedCols = p.extractUsedCols(parentUsedCols) + p.children[0].PruneColumns(parentUsedCols) + p.SetSchema(p.children[0].Schema().Clone()) + p.Schema().Append(windowColumn) +} + +func (p *LogicalWindowFunc) extractUsedCols(parentUsedCols []*expression.Column) []*expression.Column { + for _, arg := range p.Desc.Args { + parentUsedCols = append(parentUsedCols, expression.ExtractColumns(arg)...) + } + for _, by := range p.ByItems { + parentUsedCols = append(parentUsedCols, expression.ExtractColumns(by.Expr)...) + } + for _, expr := range p.PartitionBy { + parentUsedCols = append(parentUsedCols, expression.ExtractColumns(expr)...) + } + return parentUsedCols +} diff --git a/planner/core/stats.go b/planner/core/stats.go index ad1c480fdd34c..2afa1977c7ad3 100644 --- a/planner/core/stats.go +++ b/planner/core/stats.go @@ -378,3 +378,21 @@ func (p *LogicalMaxOneRow) deriveStats() (*property.StatsInfo, error) { p.stats = getSingletonStats(p.Schema().Len()) return p.stats, nil } + +func (p *LogicalWindowFunc) deriveStats() (*property.StatsInfo, error) { + if p.stats != nil { + return p.stats, nil + } + childProfile, err := p.children[0].deriveStats() + if err != nil { + return nil, err + } + childLen := len(childProfile.Cardinality) + p.stats = &property.StatsInfo{ + RowCount: childProfile.RowCount, + Cardinality: make([]float64, childLen+1), + } + copy(p.stats.Cardinality, childProfile.Cardinality) + p.stats.Cardinality[childLen] = childProfile.RowCount + return p.stats, nil +} diff --git a/planner/core/stringer.go b/planner/core/stringer.go index 6cd078354469a..b94b8baca70fb 100644 --- a/planner/core/stringer.go +++ b/planner/core/stringer.go @@ -220,6 +220,10 @@ func toString(in Plan, strs []string, idxs []int) ([]string, []int) { if x.SelectPlan != nil { str = fmt.Sprintf("%s->Insert", ToString(x.SelectPlan)) } + case *LogicalWindowFunc: + str = fmt.Sprintf("WindowFunc(%s)", x.Desc.String()) + case *PhysicalWindowFunc: + str = fmt.Sprintf("WindowFunc(%s)", x.Desc.String()) default: str = fmt.Sprintf("%T", in) } From 6244626c7b43686b64dd4daab7fa1bb805d29808 Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Tue, 11 Dec 2018 19:24:03 +0800 Subject: [PATCH 02/17] address comments --- planner/core/exhaust_physical_plans.go | 8 ++++---- planner/core/explain.go | 4 ++-- planner/core/initialize.go | 16 ++++++++-------- planner/core/logical_plan_builder.go | 10 +++++----- planner/core/logical_plans.go | 14 +++++++------- planner/core/physical_plans.go | 10 +++++----- planner/core/rule_column_pruning.go | 6 +++--- planner/core/stats.go | 2 +- planner/core/stringer.go | 8 ++++---- 9 files changed, 39 insertions(+), 39 deletions(-) diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index 4efe7fb1747a4..75ef881e0cde0 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -779,13 +779,13 @@ func (la *LogicalApply) exhaustPhysicalPlans(prop *property.PhysicalProperty) [] return []PhysicalPlan{apply} } -func (p *LogicalWindowFunc) exhaustPhysicalPlans(prop *property.PhysicalProperty) []PhysicalPlan { +func (p *LogicalWindow) exhaustPhysicalPlans(prop *property.PhysicalProperty) []PhysicalPlan { if !matchItems(prop, p.ByItems) { return nil } - window := PhysicalWindowFunc{ - Desc: p.Desc, - PartitionBy: p.PartitionBy, + window := PhysicalWindow{ + WindowFuncDesc: p.WindowFuncDesc, + PartitionBy: p.PartitionBy, }.Init(p.ctx, p.stats.ScaleByExpectCnt(prop.ExpectedCnt), &property.PhysicalProperty{ExpectedCnt: math.MaxFloat64}) window.SetSchema(p.Schema()) return []PhysicalPlan{window} diff --git a/planner/core/explain.go b/planner/core/explain.go index 61ace9512db49..ddce695e8d2db 100644 --- a/planner/core/explain.go +++ b/planner/core/explain.go @@ -299,6 +299,6 @@ func (p *PhysicalTopN) ExplainInfo() string { } // ExplainInfo implements PhysicalPlan interface. -func (p *PhysicalWindowFunc) ExplainInfo() string { - return p.Desc.String() +func (p *PhysicalWindow) ExplainInfo() string { + return p.WindowFuncDesc.String() } diff --git a/planner/core/initialize.go b/planner/core/initialize.go index 8b76ea8153ff6..ff2a3dbc7b059 100644 --- a/planner/core/initialize.go +++ b/planner/core/initialize.go @@ -81,8 +81,8 @@ const ( TypeTableReader = "TableReader" // TypeIndexReader is the type of IndexReader. TypeIndexReader = "IndexReader" - // TypeWindowFunc is the type of WindowFunc. - TypeWindowFunc = "WindowFunc" + // TypeWindow is the type of Window. + TypeWindow = "Window" ) // Init initializes LogicalAggregation. @@ -233,15 +233,15 @@ func (p PhysicalMaxOneRow) Init(ctx sessionctx.Context, stats *property.StatsInf return &p } -// Init initializes LogicalWindowFunc. -func (p LogicalWindowFunc) Init(ctx sessionctx.Context) *LogicalWindowFunc { - p.baseLogicalPlan = newBaseLogicalPlan(ctx, TypeWindowFunc, &p) +// Init initializes LogicalWindow. +func (p LogicalWindow) Init(ctx sessionctx.Context) *LogicalWindow { + p.baseLogicalPlan = newBaseLogicalPlan(ctx, TypeWindow, &p) return &p } -// Init initializes PhysicalWindowFunc. -func (p PhysicalWindowFunc) Init(ctx sessionctx.Context, stats *property.StatsInfo, props ...*property.PhysicalProperty) *PhysicalWindowFunc { - p.basePhysicalPlan = newBasePhysicalPlan(ctx, TypeWindowFunc, &p) +// Init initializes PhysicalWindow. +func (p PhysicalWindow) Init(ctx sessionctx.Context, stats *property.StatsInfo, props ...*property.PhysicalProperty) *PhysicalWindow { + p.basePhysicalPlan = newBasePhysicalPlan(ctx, TypeWindow, &p) p.childrenReqProps = props p.stats = stats return &p diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index b1a84b71b43d5..45ef14c3a0f0d 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -2519,7 +2519,7 @@ func (b *PlanBuilder) buildWindowFunctions(p LogicalPlan, sel *ast.SelectStmt, w return proj, oldLen, nil } -func (b *PlanBuilder) buildWindowFunction(p LogicalPlan, expr *ast.WindowFuncExpr, aggMap map[*ast.AggregateFuncExpr]int) (*LogicalWindowFunc, error) { +func (b *PlanBuilder) buildWindowFunction(p LogicalPlan, expr *ast.WindowFuncExpr, aggMap map[*ast.AggregateFuncExpr]int) (*LogicalWindow, error) { spec := expr.Spec // Add sort for partition by and order by var byItems []*ast.ByItem @@ -2557,10 +2557,10 @@ func (b *PlanBuilder) buildWindowFunction(p LogicalPlan, expr *ast.WindowFuncExp } desc := aggregation.NewWindowFuncDesc(b.ctx, expr.F, newArgList) - window := LogicalWindowFunc{ - Desc: desc, - PartitionBy: partitionBy, - ByItems: orderByItems, + window := LogicalWindow{ + WindowFuncDesc: desc, + PartitionBy: partitionBy, + ByItems: orderByItems, }.Init(b.ctx) schema := p.Schema().Clone() schema.Append(&expression.Column{ diff --git a/planner/core/logical_plans.go b/planner/core/logical_plans.go index 1c25dffbc0910..6e56390f019e0 100644 --- a/planner/core/logical_plans.go +++ b/planner/core/logical_plans.go @@ -42,7 +42,7 @@ var ( _ LogicalPlan = &LogicalSort{} _ LogicalPlan = &LogicalLock{} _ LogicalPlan = &LogicalLimit{} - _ LogicalPlan = &LogicalWindowFunc{} + _ LogicalPlan = &LogicalWindow{} ) // JoinType contains CrossJoin, InnerJoin, LeftOuterJoin, RightOuterJoin, FullOuterJoin, SemiJoin. @@ -619,17 +619,17 @@ type LogicalLock struct { Lock ast.SelectLockType } -// LogicalWindowFunc represents a logical window function plan. -type LogicalWindowFunc struct { +// LogicalWindow represents a logical window function plan. +type LogicalWindow struct { logicalSchemaProducer - Desc *aggregation.WindowFuncDesc - ByItems []*ByItems - PartitionBy []expression.Expression + WindowFuncDesc *aggregation.WindowFuncDesc + ByItems []*ByItems + PartitionBy []expression.Expression // TODO: add frame clause } // GetWindowResultColumn returns the column storing the result of the window function. -func (p *LogicalWindowFunc) GetWindowResultColumn() *expression.Column { +func (p *LogicalWindow) GetWindowResultColumn() *expression.Column { return p.schema.Columns[p.schema.Len()-1] } diff --git a/planner/core/physical_plans.go b/planner/core/physical_plans.go index f0501e385a214..87fb705969b5e 100644 --- a/planner/core/physical_plans.go +++ b/planner/core/physical_plans.go @@ -47,7 +47,7 @@ var ( _ PhysicalPlan = &PhysicalHashJoin{} _ PhysicalPlan = &PhysicalMergeJoin{} _ PhysicalPlan = &PhysicalUnionScan{} - _ PhysicalPlan = &PhysicalWindowFunc{} + _ PhysicalPlan = &PhysicalWindow{} ) // PhysicalTableReader is the table reader in tidb. @@ -375,10 +375,10 @@ type PhysicalTableDual struct { RowCount int } -// PhysicalWindowFunc is the physical operator of window function. -type PhysicalWindowFunc struct { +// PhysicalWindow is the physical operator of window function. +type PhysicalWindow struct { physicalSchemaProducer - Desc *aggregation.WindowFuncDesc - PartitionBy []expression.Expression + WindowFuncDesc *aggregation.WindowFuncDesc + PartitionBy []expression.Expression } diff --git a/planner/core/rule_column_pruning.go b/planner/core/rule_column_pruning.go index eee8a9cf3555f..e4de752a8e1b1 100644 --- a/planner/core/rule_column_pruning.go +++ b/planner/core/rule_column_pruning.go @@ -266,7 +266,7 @@ func (p *LogicalLock) PruneColumns(parentUsedCols []*expression.Column) { } // PruneColumns implements LogicalPlan interface. -func (p *LogicalWindowFunc) PruneColumns(parentUsedCols []*expression.Column) { +func (p *LogicalWindow) PruneColumns(parentUsedCols []*expression.Column) { windowColumn := p.GetWindowResultColumn() len := 0 for _, col := range parentUsedCols { @@ -282,8 +282,8 @@ func (p *LogicalWindowFunc) PruneColumns(parentUsedCols []*expression.Column) { p.Schema().Append(windowColumn) } -func (p *LogicalWindowFunc) extractUsedCols(parentUsedCols []*expression.Column) []*expression.Column { - for _, arg := range p.Desc.Args { +func (p *LogicalWindow) extractUsedCols(parentUsedCols []*expression.Column) []*expression.Column { + for _, arg := range p.WindowFuncDesc.Args { parentUsedCols = append(parentUsedCols, expression.ExtractColumns(arg)...) } for _, by := range p.ByItems { diff --git a/planner/core/stats.go b/planner/core/stats.go index 2afa1977c7ad3..7bb155aa0b776 100644 --- a/planner/core/stats.go +++ b/planner/core/stats.go @@ -379,7 +379,7 @@ func (p *LogicalMaxOneRow) deriveStats() (*property.StatsInfo, error) { return p.stats, nil } -func (p *LogicalWindowFunc) deriveStats() (*property.StatsInfo, error) { +func (p *LogicalWindow) deriveStats() (*property.StatsInfo, error) { if p.stats != nil { return p.stats, nil } diff --git a/planner/core/stringer.go b/planner/core/stringer.go index b94b8baca70fb..86267c3aced98 100644 --- a/planner/core/stringer.go +++ b/planner/core/stringer.go @@ -220,10 +220,10 @@ func toString(in Plan, strs []string, idxs []int) ([]string, []int) { if x.SelectPlan != nil { str = fmt.Sprintf("%s->Insert", ToString(x.SelectPlan)) } - case *LogicalWindowFunc: - str = fmt.Sprintf("WindowFunc(%s)", x.Desc.String()) - case *PhysicalWindowFunc: - str = fmt.Sprintf("WindowFunc(%s)", x.Desc.String()) + case *LogicalWindow: + str = fmt.Sprintf("WindowFunc(%s)", x.WindowFuncDesc.String()) + case *PhysicalWindow: + str = fmt.Sprintf("WindowFunc(%s)", x.WindowFuncDesc.String()) default: str = fmt.Sprintf("%T", in) } From 2b8d890dc9573f486eb9ca0ea8ad12ab19b6ae0d Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Tue, 11 Dec 2018 20:05:14 +0800 Subject: [PATCH 03/17] use latest parser --- go.mod | 2 +- go.sum | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index cbecb1497d7bb..5edcb7d6bdc6a 100644 --- a/go.mod +++ b/go.mod @@ -86,4 +86,4 @@ require ( sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67 ) -replace github.com/pingcap/parser => github.com/lamxTyler/parser v0.0.0-20181210071325-a03eabfb41e7 +replace github.com/pingcap/parser => github.com/lamxTyler/parser v0.0.0-20181211113423-12b2833db91b diff --git a/go.sum b/go.sum index 0fe266faede72..87e6e12a32973 100644 --- a/go.sum +++ b/go.sum @@ -79,7 +79,7 @@ github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5 h1:2U0HzY8BJ8hVwDK github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/lamxTyler/parser v0.0.0-20181210071325-a03eabfb41e7/go.mod h1:6c1rwSy9dUuNebYdr1IMI4+/sT3/Q65MXP2UCg7/vJI= +github.com/lamxTyler/parser v0.0.0-20181211113423-12b2833db91b/go.mod h1:6c1rwSy9dUuNebYdr1IMI4+/sT3/Q65MXP2UCg7/vJI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/montanaflynn/stats v0.0.0-20180911141734-db72e6cae808 h1:pmpDGKLw4n82EtrNiLqB+xSz/JQwFOaZuMALYUHwX5s= From aa59c92187361b77d6536cd4e2df5887bfa67135 Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Wed, 19 Dec 2018 17:20:34 +0800 Subject: [PATCH 04/17] address comments --- go.mod | 2 +- go.sum | 2 +- planner/core/exhaust_physical_plans.go | 6 +- planner/core/explain.go | 1 + planner/core/logical_plan_builder.go | 107 +++++++++++++------------ planner/core/logical_plan_test.go | 33 +++++--- planner/core/logical_plans.go | 4 +- planner/core/physical_plans.go | 1 - planner/core/rule_column_pruning.go | 5 +- 9 files changed, 85 insertions(+), 76 deletions(-) diff --git a/go.mod b/go.mod index 9bbdb949db653..e9664553d4583 100644 --- a/go.mod +++ b/go.mod @@ -86,4 +86,4 @@ require ( sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67 ) -replace github.com/pingcap/parser => github.com/lamxTyler/parser v0.0.0-20181211113423-12b2833db91b +replace github.com/pingcap/parser => github.com/lamxTyler/parser v0.0.0-20181218085011-caba8be2bcd6 diff --git a/go.sum b/go.sum index b4631296b3e17..d397baa4af898 100644 --- a/go.sum +++ b/go.sum @@ -79,7 +79,7 @@ github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5 h1:2U0HzY8BJ8hVwDK github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/lamxTyler/parser v0.0.0-20181211113423-12b2833db91b/go.mod h1:6c1rwSy9dUuNebYdr1IMI4+/sT3/Q65MXP2UCg7/vJI= +github.com/lamxTyler/parser v0.0.0-20181218085011-caba8be2bcd6/go.mod h1:6c1rwSy9dUuNebYdr1IMI4+/sT3/Q65MXP2UCg7/vJI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/montanaflynn/stats v0.0.0-20180911141734-db72e6cae808 h1:pmpDGKLw4n82EtrNiLqB+xSz/JQwFOaZuMALYUHwX5s= diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index e3f3cd3ddc66a..fd424a7a5e3ca 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -786,13 +786,13 @@ func (la *LogicalApply) exhaustPhysicalPlans(prop *property.PhysicalProperty) [] } func (p *LogicalWindow) exhaustPhysicalPlans(prop *property.PhysicalProperty) []PhysicalPlan { - if !matchItems(prop, p.ByItems) { + childProperty := &property.PhysicalProperty{ExpectedCnt: math.MaxFloat64, Items: p.ByItems, Enforced: true} + if !prop.IsPrefix(childProperty) { return nil } window := PhysicalWindow{ WindowFuncDesc: p.WindowFuncDesc, - PartitionBy: p.PartitionBy, - }.Init(p.ctx, p.stats.ScaleByExpectCnt(prop.ExpectedCnt), &property.PhysicalProperty{ExpectedCnt: math.MaxFloat64}) + }.Init(p.ctx, p.stats.ScaleByExpectCnt(prop.ExpectedCnt), childProperty) window.SetSchema(p.Schema()) return []PhysicalPlan{window} } diff --git a/planner/core/explain.go b/planner/core/explain.go index ddce695e8d2db..44e9ef4c81099 100644 --- a/planner/core/explain.go +++ b/planner/core/explain.go @@ -300,5 +300,6 @@ func (p *PhysicalTopN) ExplainInfo() string { // ExplainInfo implements PhysicalPlan interface. func (p *PhysicalWindow) ExplainInfo() string { + // TODO: Add explain info for partition by, order by and frame. return p.WindowFuncDesc.String() } diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index 45ef14c3a0f0d..bf85d177540d8 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -33,6 +33,7 @@ import ( "github.com/pingcap/tidb/expression/aggregation" "github.com/pingcap/tidb/infoschema" "github.com/pingcap/tidb/metrics" + "github.com/pingcap/tidb/planner/property" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/statistics" "github.com/pingcap/tidb/table" @@ -596,17 +597,27 @@ func (b *PlanBuilder) buildProjectionField(id, position int, field *ast.SelectFi } // buildProjection returns a Projection plan and non-aux columns length. -func (b *PlanBuilder) buildProjection(p LogicalPlan, fields []*ast.SelectField, mapper map[*ast.AggregateFuncExpr]int) (LogicalPlan, int, error) { +func (b *PlanBuilder) buildProjection(p LogicalPlan, fields []*ast.SelectField, mapper map[*ast.AggregateFuncExpr]int, considerWindow bool) (LogicalPlan, int, error) { b.optFlag |= flagEliminateProjection b.curClause = fieldList proj := LogicalProjection{Exprs: make([]expression.Expression, 0, len(fields))}.Init(b.ctx) schema := expression.NewSchema(make([]*expression.Column, 0, len(fields))...) oldLen := 0 - for _, field := range fields { - // Window functions that exists in field list could only be processed after having, - // so we add a fake placeholder here. - if ast.HasWindowFlag(field.Expr) { - expr := expression.Zero + for i, field := range fields { + if !field.Auxiliary { + oldLen++ + } + + hasWindow := ast.HasWindowFlag(field.Expr) + // When `considerWindow` is false, we will only build fields for non-window functions, so we add fake placeholders. + // When `considerWindow` is true, all the non-window fields have been built, so we just use the schema columns. + if (considerWindow && !hasWindow) || (!considerWindow && hasWindow) { + var expr expression.Expression + if hasWindow { + expr = expression.Zero + } else { + expr = p.Schema().Columns[i] + } proj.Exprs = append(proj.Exprs, expr) col := b.buildProjectionField(proj.id, schema.Len()+1, field, expr) schema.Append(col) @@ -622,10 +633,6 @@ func (b *PlanBuilder) buildProjection(p LogicalPlan, fields []*ast.SelectField, col := b.buildProjectionField(proj.id, schema.Len()+1, field, newExpr) schema.Append(col) - - if !field.Auxiliary { - oldLen++ - } } proj.SetSchema(schema) proj.SetChildren(p) @@ -1853,7 +1860,8 @@ func (b *PlanBuilder) buildSelect(sel *ast.SelectStmt) (p LogicalPlan, err error } var oldLen int - p, oldLen, err = b.buildProjection(p, sel.Fields.Fields, totalMap) + // `considerWindow` is false now because we can only process window functions after having clause. + p, oldLen, err = b.buildProjection(p, sel.Fields.Fields, totalMap, false) if err != nil { return nil, errors.Trace(err) } @@ -1867,7 +1875,8 @@ func (b *PlanBuilder) buildSelect(sel *ast.SelectStmt) (p LogicalPlan, err error } if hasWindow { - p, oldLen, err = b.buildWindowFunctions(p, sel, windowMap) + // Now we build the window function fields. + p, oldLen, err = b.buildProjection(p, sel.Fields.Fields, windowMap, true) if err != nil { return nil, errors.Trace(err) } @@ -2484,66 +2493,59 @@ func (b *PlanBuilder) buildDelete(delete *ast.DeleteStmt) (Plan, error) { return del, nil } -func (b *PlanBuilder) buildWindowFunctions(p LogicalPlan, sel *ast.SelectStmt, windowMap map[*ast.AggregateFuncExpr]int) (LogicalPlan, int, error) { +func (b *PlanBuilder) buildProjectionForWindow(p LogicalPlan, byItems []*ast.ByItem, aggMap map[*ast.AggregateFuncExpr]int) (LogicalPlan, []property.Item, error) { b.optFlag |= flagEliminateProjection - b.curClause = fieldList - fields := sel.Fields.Fields - proj := LogicalProjection{Exprs: make([]expression.Expression, 0, len(fields))}.Init(b.ctx) - schema := expression.NewSchema(make([]*expression.Column, 0, len(fields))...) - oldLen := 0 - for i, field := range fields { - if !field.Auxiliary { - oldLen++ + exprs := make([]property.Item, 0, len(byItems)) + proj := LogicalProjection{Exprs: make([]expression.Expression, 0, len(p.Schema().Columns)+len(byItems))}.Init(b.ctx) + schema := expression.NewSchema(make([]*expression.Column, 0, len(p.Schema().Columns)+len(byItems))...) + for _, col := range p.Schema().Columns { + proj.Exprs = append(proj.Exprs, col) + schema.Append(col) + } + transformer := &itemTransformer{} + for _, item := range byItems { + newExpr, _ := item.Expr.Accept(transformer) + item.Expr = newExpr.(ast.ExprNode) + it, np, err := b.rewrite(item.Expr, p, aggMap, true) + if err != nil { + return nil, nil, errors.Trace(err) } - // it has been built in previous projection - if !ast.HasWindowFlag(field.Expr) { - col := p.Schema().Columns[i] - proj.Exprs = append(proj.Exprs, col) - newCol := b.buildProjectionField(proj.id, schema.Len()+1, field, col) - schema.Append(newCol) + p = np + if col, ok := it.(*expression.Column); ok { + exprs = append(exprs, property.Item{Col: col, Desc: item.Desc}) continue } - newExpr, np, err := b.rewrite(field.Expr, p, windowMap, true) - if err != nil { - return nil, 0, errors.Trace(err) + proj.Exprs = append(proj.Exprs, it) + col := &expression.Column{ + ColName: model.NewCIStr(fmt.Sprintf("%d_proj_window_%d", p.ID(), schema.Len())), + UniqueID: b.ctx.GetSessionVars().AllocPlanColumnID(), + RetType: it.GetType(), } - - p = np - proj.Exprs = append(proj.Exprs, newExpr) - - col := b.buildProjectionField(proj.id, schema.Len()+1, field, newExpr) schema.Append(col) + exprs = append(exprs, property.Item{Col: col, Desc: item.Desc}) } proj.SetSchema(schema) proj.SetChildren(p) - return proj, oldLen, nil + return proj, exprs, nil } func (b *PlanBuilder) buildWindowFunction(p LogicalPlan, expr *ast.WindowFuncExpr, aggMap map[*ast.AggregateFuncExpr]int) (*LogicalWindow, error) { spec := expr.Spec // Add sort for partition by and order by - var byItems []*ast.ByItem + var items []*ast.ByItem if spec.PartitionBy != nil { - byItems = append(byItems, spec.PartitionBy.Items...) + items = append(items, spec.PartitionBy.Items...) } if spec.OrderBy != nil { - byItems = append(byItems, spec.OrderBy.Items...) + items = append(items, spec.OrderBy.Items...) } - var partitionBy []expression.Expression - var orderByItems []*ByItems - if len(byItems) > 0 { - s, err := b.buildSort(p, byItems, nil) + var byItems []property.Item + if len(items) > 0 { + var err error + p, byItems, err = b.buildProjectionForWindow(p, items, aggMap) if err != nil { return nil, err } - if spec.PartitionBy != nil { - partitionBy = make([]expression.Expression, 0, len(spec.PartitionBy.Items)) - for i := 0; i < len(spec.PartitionBy.Items); i++ { - partitionBy = append(partitionBy, s.ByItems[i].Expr) - } - } - orderByItems = s.ByItems - p = s } newArgList := make([]expression.Expression, 0, len(expr.Args)) @@ -2559,8 +2561,7 @@ func (b *PlanBuilder) buildWindowFunction(p LogicalPlan, expr *ast.WindowFuncExp desc := aggregation.NewWindowFuncDesc(b.ctx, expr.F, newArgList) window := LogicalWindow{ WindowFuncDesc: desc, - PartitionBy: partitionBy, - ByItems: orderByItems, + ByItems: byItems, }.Init(b.ctx) schema := p.Schema().Clone() schema.Append(&expression.Column{ diff --git a/planner/core/logical_plan_test.go b/planner/core/logical_plan_test.go index 9936338abab39..d04f9ab3c836f 100644 --- a/planner/core/logical_plan_test.go +++ b/planner/core/logical_plan_test.go @@ -1881,16 +1881,24 @@ func (s *testPlanSuite) TestWindowFunction(c *C) { result string }{ { - sql: "select a, avg(a) over() from t", - result: "DataScan(t)->WindowFunc(avg(test.t.a))->Projection", + sql: "select a, avg(a) over(partition by a) from t", + result: "TableReader(Table(t))->WindowFunc(avg(test.t.a))->Projection", }, { - sql: "select a, avg(a) over(partition by a) from t", - result: "DataScan(t)->Sort->WindowFunc(avg(test.t.a))->Projection", + sql: "select a, avg(a) over(partition by b) from t", + result: "TableReader(Table(t))->Sort->WindowFunc(avg(test.t.a))->Projection", + }, + { + sql: "select a, avg(a) over(partition by (a+1)) from t", + result: "TableReader(Table(t))->Projection->Sort->WindowFunc(avg(test.t.a))->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->WindowFunc(avg(test.t.a))->Projection", }, { sql: "select a, b as a, avg(a) over(partition by a) from t", - result: "DataScan(t)->Sort->WindowFunc(avg(test.t.a))->Projection", + result: "TableReader(Table(t))->WindowFunc(avg(test.t.a))->Projection", }, { sql: "select a, b as z, sum(z) over() from t", @@ -1898,27 +1906,27 @@ func (s *testPlanSuite) TestWindowFunction(c *C) { }, { sql: "select a, b as z from t order by (sum(z) over())", - result: "DataScan(t)->WindowFunc(sum(test.t.z))->Sort->Projection", + result: "TableReader(Table(t))->WindowFunc(sum(test.t.z))->Sort->Projection", }, { sql: "select sum(avg(a)) over() from t", - result: "DataScan(t)->Aggr(avg(test.t.a))->WindowFunc(sum(sel_agg_2))->Projection", + result: "TableReader(Table(t)->StreamAgg)->StreamAgg->WindowFunc(sum(sel_agg_2))->Projection", }, { sql: "select b from t order by(sum(a) over())", - result: "DataScan(t)->WindowFunc(sum(test.t.a))->Sort->Projection", + result: "TableReader(Table(t))->WindowFunc(sum(test.t.a))->Sort->Projection", }, { sql: "select b from t order by(sum(a) over(partition by a))", - result: "DataScan(t)->Sort->WindowFunc(sum(test.t.a))->Sort->Projection", + result: "TableReader(Table(t))->WindowFunc(sum(test.t.a))->Sort->Projection", }, { sql: "select b from t order by(sum(avg(a)) over())", - result: "DataScan(t)->Aggr(avg(test.t.a),firstrow(test.t.b))->WindowFunc(sum(sel_agg_2))->Sort->Projection", + result: "TableReader(Table(t)->StreamAgg)->StreamAgg->WindowFunc(sum(sel_agg_2))->Sort->Projection", }, { sql: "select a from t having (select sum(a) over() as w from t tt where a > t.a)", - result: "Apply{DataScan(t)->DataScan(tt)->WindowFunc(sum(tt.a))->MaxOneRow->Sel([w])}->Projection", + result: "Apply{TableReader(Table(t))->TableReader(Table(t)->Sel([gt(tt.a, test.t.a)]))->WindowFunc(sum(tt.a))->MaxOneRow->Sel([w])}->Projection", }, { sql: "select avg(a) over() as w from t having w > 1", @@ -1948,6 +1956,9 @@ func (s *testPlanSuite) TestWindowFunction(c *C) { c.Assert(err, IsNil) p, err = logicalOptimize(builder.optFlag, p.(LogicalPlan)) c.Assert(err, IsNil) + lp, ok := p.(LogicalPlan) + c.Assert(ok, IsTrue) + p, err = physicalOptimize(lp) c.Assert(ToString(p), Equals, tt.result, comment) } } diff --git a/planner/core/logical_plans.go b/planner/core/logical_plans.go index 6e56390f019e0..bcefdd223fb5f 100644 --- a/planner/core/logical_plans.go +++ b/planner/core/logical_plans.go @@ -22,6 +22,7 @@ import ( "github.com/pingcap/parser/mysql" "github.com/pingcap/tidb/expression" "github.com/pingcap/tidb/expression/aggregation" + "github.com/pingcap/tidb/planner/property" "github.com/pingcap/tidb/statistics" "github.com/pingcap/tidb/table" "github.com/pingcap/tidb/types" @@ -624,8 +625,7 @@ type LogicalWindow struct { logicalSchemaProducer WindowFuncDesc *aggregation.WindowFuncDesc - ByItems []*ByItems - PartitionBy []expression.Expression + ByItems []property.Item // TODO: add frame clause } diff --git a/planner/core/physical_plans.go b/planner/core/physical_plans.go index 87fb705969b5e..4f404e88d9f2c 100644 --- a/planner/core/physical_plans.go +++ b/planner/core/physical_plans.go @@ -380,5 +380,4 @@ type PhysicalWindow struct { physicalSchemaProducer WindowFuncDesc *aggregation.WindowFuncDesc - PartitionBy []expression.Expression } diff --git a/planner/core/rule_column_pruning.go b/planner/core/rule_column_pruning.go index e4de752a8e1b1..bf2d86a916575 100644 --- a/planner/core/rule_column_pruning.go +++ b/planner/core/rule_column_pruning.go @@ -287,10 +287,7 @@ func (p *LogicalWindow) extractUsedCols(parentUsedCols []*expression.Column) []* parentUsedCols = append(parentUsedCols, expression.ExtractColumns(arg)...) } for _, by := range p.ByItems { - parentUsedCols = append(parentUsedCols, expression.ExtractColumns(by.Expr)...) - } - for _, expr := range p.PartitionBy { - parentUsedCols = append(parentUsedCols, expression.ExtractColumns(expr)...) + parentUsedCols = append(parentUsedCols, by.Col) } return parentUsedCols } From 33e76f9cf7d6d0e7862905a0657d2f4b09d53cce Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Wed, 26 Dec 2018 16:02:52 +0800 Subject: [PATCH 05/17] fix gofmt --- go.sum | 3 --- planner/core/errors.go | 50 +++++++++++++++++++++--------------------- planner/core/stats.go | 1 + 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/go.sum b/go.sum index 2e85ae8393433..c6977fa3d8415 100644 --- a/go.sum +++ b/go.sum @@ -81,9 +81,6 @@ github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5 h1:2U0HzY8BJ8hVwDK github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/lamxTyler/parser v0.0.0-20181218085011-caba8be2bcd6 h1:NNtwX/wAmZu6vRMvp4ErBNsj3zPSjvZg+CjSqU1QOr4= -github.com/lamxTyler/parser v0.0.0-20181218085011-caba8be2bcd6/go.mod h1:6c1rwSy9dUuNebYdr1IMI4+/sT3/Q65MXP2UCg7/vJI= -github.com/lamxTyler/parser v0.0.0-20181226064458-13cec0b9d426 h1:XFZHJpgEjO8eYBkXNzChno1o8Yp8Wi8IFD31K+msq8k= github.com/lamxTyler/parser v0.0.0-20181226064458-13cec0b9d426/go.mod h1:6c1rwSy9dUuNebYdr1IMI4+/sT3/Q65MXP2UCg7/vJI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= diff --git a/planner/core/errors.go b/planner/core/errors.go index 042ee9e7b277f..0541b4b8d7bfc 100644 --- a/planner/core/errors.go +++ b/planner/core/errors.go @@ -66,31 +66,31 @@ var ( ErrSchemaChanged = terror.ClassOptimizer.New(codeSchemaChanged, "Schema has changed") ErrTablenameNotAllowedHere = terror.ClassOptimizer.New(codeTablenameNotAllowedHere, "Table '%s' from one of the %ss cannot be used in %s") - ErrWrongUsage = terror.ClassOptimizer.New(codeWrongUsage, mysql.MySQLErrName[mysql.ErrWrongUsage]) - ErrAmbiguous = terror.ClassOptimizer.New(codeAmbiguous, mysql.MySQLErrName[mysql.ErrNonUniq]) - ErrUnknown = terror.ClassOptimizer.New(codeUnknown, mysql.MySQLErrName[mysql.ErrUnknown]) - ErrUnknownColumn = terror.ClassOptimizer.New(codeUnknownColumn, mysql.MySQLErrName[mysql.ErrBadField]) - ErrUnknownTable = terror.ClassOptimizer.New(codeUnknownTable, mysql.MySQLErrName[mysql.ErrUnknownTable]) - ErrWrongArguments = terror.ClassOptimizer.New(codeWrongArguments, mysql.MySQLErrName[mysql.ErrWrongArguments]) - ErrWrongNumberOfColumnsInSelect = terror.ClassOptimizer.New(codeWrongNumberOfColumnsInSelect, mysql.MySQLErrName[mysql.ErrWrongNumberOfColumnsInSelect]) - ErrBadGeneratedColumn = terror.ClassOptimizer.New(codeBadGeneratedColumn, mysql.MySQLErrName[mysql.ErrBadGeneratedColumn]) - ErrFieldNotInGroupBy = terror.ClassOptimizer.New(codeFieldNotInGroupBy, mysql.MySQLErrName[mysql.ErrFieldNotInGroupBy]) - ErrBadTable = terror.ClassOptimizer.New(codeBadTable, mysql.MySQLErrName[mysql.ErrBadTable]) - ErrKeyDoesNotExist = terror.ClassOptimizer.New(codeKeyDoesNotExist, mysql.MySQLErrName[mysql.ErrKeyDoesNotExist]) - ErrOperandColumns = terror.ClassOptimizer.New(codeOperandColumns, mysql.MySQLErrName[mysql.ErrOperandColumns]) - ErrInvalidWildCard = terror.ClassOptimizer.New(codeInvalidWildCard, "Wildcard fields without any table name appears in wrong place") - ErrInvalidGroupFuncUse = terror.ClassOptimizer.New(codeInvalidGroupFuncUse, mysql.MySQLErrName[mysql.ErrInvalidGroupFuncUse]) - ErrIllegalReference = terror.ClassOptimizer.New(codeIllegalReference, mysql.MySQLErrName[mysql.ErrIllegalReference]) - ErrNoDB = terror.ClassOptimizer.New(codeNoDB, mysql.MySQLErrName[mysql.ErrNoDB]) - ErrUnknownExplainFormat = terror.ClassOptimizer.New(codeUnknownExplainFormat, mysql.MySQLErrName[mysql.ErrUnknownExplainFormat]) - ErrWrongGroupField = terror.ClassOptimizer.New(codeWrongGroupField, mysql.MySQLErrName[mysql.ErrWrongGroupField]) - ErrDupFieldName = terror.ClassOptimizer.New(codeDupFieldName, mysql.MySQLErrName[mysql.ErrDupFieldName]) - ErrNonUpdatableTable = terror.ClassOptimizer.New(codeNonUpdatableTable, mysql.MySQLErrName[mysql.ErrNonUpdatableTable]) - ErrInternal = terror.ClassOptimizer.New(codeInternal, mysql.MySQLErrName[mysql.ErrInternal]) - ErrMixOfGroupFuncAndFields = terror.ClassOptimizer.New(codeMixOfGroupFuncAndFields, "In aggregated query without GROUP BY, expression #%d of SELECT list contains nonaggregated column '%s'; this is incompatible with sql_mode=only_full_group_by") - ErrNonUniqTable = terror.ClassOptimizer.New(codeNonUniqTable, mysql.MySQLErrName[mysql.ErrNonuniqTable]) - ErrWrongValueCountOnRow = terror.ClassOptimizer.New(mysql.ErrWrongValueCountOnRow, mysql.MySQLErrName[mysql.ErrWrongValueCountOnRow]) - ErrViewInvalid = terror.ClassOptimizer.New(mysql.ErrViewInvalid, mysql.MySQLErrName[mysql.ErrViewInvalid]) + ErrWrongUsage = terror.ClassOptimizer.New(codeWrongUsage, mysql.MySQLErrName[mysql.ErrWrongUsage]) + ErrAmbiguous = terror.ClassOptimizer.New(codeAmbiguous, mysql.MySQLErrName[mysql.ErrNonUniq]) + ErrUnknown = terror.ClassOptimizer.New(codeUnknown, mysql.MySQLErrName[mysql.ErrUnknown]) + ErrUnknownColumn = terror.ClassOptimizer.New(codeUnknownColumn, mysql.MySQLErrName[mysql.ErrBadField]) + ErrUnknownTable = terror.ClassOptimizer.New(codeUnknownTable, mysql.MySQLErrName[mysql.ErrUnknownTable]) + ErrWrongArguments = terror.ClassOptimizer.New(codeWrongArguments, mysql.MySQLErrName[mysql.ErrWrongArguments]) + ErrWrongNumberOfColumnsInSelect = terror.ClassOptimizer.New(codeWrongNumberOfColumnsInSelect, mysql.MySQLErrName[mysql.ErrWrongNumberOfColumnsInSelect]) + ErrBadGeneratedColumn = terror.ClassOptimizer.New(codeBadGeneratedColumn, mysql.MySQLErrName[mysql.ErrBadGeneratedColumn]) + ErrFieldNotInGroupBy = terror.ClassOptimizer.New(codeFieldNotInGroupBy, mysql.MySQLErrName[mysql.ErrFieldNotInGroupBy]) + ErrBadTable = terror.ClassOptimizer.New(codeBadTable, mysql.MySQLErrName[mysql.ErrBadTable]) + ErrKeyDoesNotExist = terror.ClassOptimizer.New(codeKeyDoesNotExist, mysql.MySQLErrName[mysql.ErrKeyDoesNotExist]) + ErrOperandColumns = terror.ClassOptimizer.New(codeOperandColumns, mysql.MySQLErrName[mysql.ErrOperandColumns]) + ErrInvalidWildCard = terror.ClassOptimizer.New(codeInvalidWildCard, "Wildcard fields without any table name appears in wrong place") + ErrInvalidGroupFuncUse = terror.ClassOptimizer.New(codeInvalidGroupFuncUse, mysql.MySQLErrName[mysql.ErrInvalidGroupFuncUse]) + ErrIllegalReference = terror.ClassOptimizer.New(codeIllegalReference, mysql.MySQLErrName[mysql.ErrIllegalReference]) + ErrNoDB = terror.ClassOptimizer.New(codeNoDB, mysql.MySQLErrName[mysql.ErrNoDB]) + ErrUnknownExplainFormat = terror.ClassOptimizer.New(codeUnknownExplainFormat, mysql.MySQLErrName[mysql.ErrUnknownExplainFormat]) + ErrWrongGroupField = terror.ClassOptimizer.New(codeWrongGroupField, mysql.MySQLErrName[mysql.ErrWrongGroupField]) + ErrDupFieldName = terror.ClassOptimizer.New(codeDupFieldName, mysql.MySQLErrName[mysql.ErrDupFieldName]) + ErrNonUpdatableTable = terror.ClassOptimizer.New(codeNonUpdatableTable, mysql.MySQLErrName[mysql.ErrNonUpdatableTable]) + ErrInternal = terror.ClassOptimizer.New(codeInternal, mysql.MySQLErrName[mysql.ErrInternal]) + ErrMixOfGroupFuncAndFields = terror.ClassOptimizer.New(codeMixOfGroupFuncAndFields, "In aggregated query without GROUP BY, expression #%d of SELECT list contains nonaggregated column '%s'; this is incompatible with sql_mode=only_full_group_by") + ErrNonUniqTable = terror.ClassOptimizer.New(codeNonUniqTable, mysql.MySQLErrName[mysql.ErrNonuniqTable]) + ErrWrongValueCountOnRow = terror.ClassOptimizer.New(mysql.ErrWrongValueCountOnRow, mysql.MySQLErrName[mysql.ErrWrongValueCountOnRow]) + ErrViewInvalid = terror.ClassOptimizer.New(mysql.ErrViewInvalid, mysql.MySQLErrName[mysql.ErrViewInvalid]) ErrWindowInvalidWindowFuncUse = terror.ClassOptimizer.New(codeWindowInvalidWindowFuncUse, mysql.MySQLErrName[mysql.ErrWindowInvalidWindowFuncUse]) ErrWindowInvalidWindowFuncAliasUse = terror.ClassOptimizer.New(codeWindowInvalidWindowFuncAliasUse, mysql.MySQLErrName[mysql.ErrWindowInvalidWindowFuncAliasUse]) ) diff --git a/planner/core/stats.go b/planner/core/stats.go index e0bdfd82cd382..32972f6132a33 100644 --- a/planner/core/stats.go +++ b/planner/core/stats.go @@ -329,6 +329,7 @@ func (p *LogicalMaxOneRow) DeriveStats(childStats []*property.StatsInfo) (*prope return p.stats, nil } +// DeriveStats implement LogicalPlan DeriveStats interface. func (p *LogicalWindow) DeriveStats(childStats []*property.StatsInfo) (*property.StatsInfo, error) { childProfile := childStats[0] childLen := len(childProfile.Cardinality) From db1305290f5ad16a64296d04838ed8815c6a6c14 Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Wed, 26 Dec 2018 19:28:55 +0800 Subject: [PATCH 06/17] address comment --- planner/core/expression_rewriter.go | 2 +- planner/core/logical_plan_builder.go | 14 +++++++------- planner/core/logical_plan_test.go | 4 ++++ planner/core/logical_plans.go | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/planner/core/expression_rewriter.go b/planner/core/expression_rewriter.go index f20b3446a3859..4bae008b4d74b 100644 --- a/planner/core/expression_rewriter.go +++ b/planner/core/expression_rewriter.go @@ -314,7 +314,7 @@ func (er *expressionRewriter) Enter(inNode ast.Node) (ast.Node, bool) { func (er *expressionRewriter) handleWindowFunction(v *ast.WindowFuncExpr) (ast.Node, bool) { windowPlan, err := er.b.buildWindowFunction(er.p, v, er.aggrMap) if err != nil { - er.err = errors.Trace(err) + er.err = err return v, false } er.ctxStack = append(er.ctxStack, windowPlan.GetWindowResultColumn()) diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index 44626c9ecb055..4538aed36c28c 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -1240,7 +1240,7 @@ func (b *PlanBuilder) resolveWindowFunction(sel *ast.SelectStmt, p LogicalPlan) } n, ok := field.Expr.Accept(extractor) if !ok { - return nil, errors.Trace(extractor.err) + return nil, extractor.err } field.Expr = n.(ast.ExprNode) } @@ -1820,11 +1820,11 @@ func (b *PlanBuilder) buildSelect(sel *ast.SelectStmt) (p LogicalPlan, err error } } - hasWindow := b.detectSelectWindow(sel) - if hasWindow { + hasWindowFuncField := b.detectSelectWindow(sel) + if hasWindowFuncField { windowMap, err = b.resolveWindowFunction(sel, p) if err != nil { - return nil, errors.Trace(err) + return nil, err } } // We must resolve having and order by clause before build projection, @@ -1874,11 +1874,11 @@ func (b *PlanBuilder) buildSelect(sel *ast.SelectStmt) (p LogicalPlan, err error } } - if hasWindow { + if hasWindowFuncField { // Now we build the window function fields. p, oldLen, err = b.buildProjection(p, sel.Fields.Fields, windowMap, true) if err != nil { - return nil, errors.Trace(err) + return nil, err } } @@ -2565,7 +2565,7 @@ func (b *PlanBuilder) buildProjectionForWindow(p LogicalPlan, byItems []*ast.ByI item.Expr = newExpr.(ast.ExprNode) it, np, err := b.rewrite(item.Expr, p, aggMap, true) if err != nil { - return nil, nil, errors.Trace(err) + return nil, nil, err } p = np if col, ok := it.(*expression.Column); ok { diff --git a/planner/core/logical_plan_test.go b/planner/core/logical_plan_test.go index 02da54aeb5031..ade57011c52ec 100644 --- a/planner/core/logical_plan_test.go +++ b/planner/core/logical_plan_test.go @@ -1963,6 +1963,10 @@ func (s *testPlanSuite) TestWindowFunction(c *C) { sql: "select avg(a) over() as w from t having w > 1", result: "[planner:3594]You cannot use the alias 'w' of an expression containing a window function in this context.'", }, + { + sql: "select sum(a) over() as sum_a from t group by sum_a", + result: "[planner:1247]Reference 'sum_a' not supported (reference to window function)", + }, } s.Parser.EnableWindowFunc(true) diff --git a/planner/core/logical_plans.go b/planner/core/logical_plans.go index bcefdd223fb5f..1a625a93ad709 100644 --- a/planner/core/logical_plans.go +++ b/planner/core/logical_plans.go @@ -625,7 +625,7 @@ type LogicalWindow struct { logicalSchemaProducer WindowFuncDesc *aggregation.WindowFuncDesc - ByItems []property.Item + ByItems []property.Item // ByItems is composed of `PARTITION BY` and `ORDER BY` items. // TODO: add frame clause } From ecf4ef8f76c39171ed93317d1abcb2e3bca4d460 Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Wed, 26 Dec 2018 19:35:10 +0800 Subject: [PATCH 07/17] address comments --- planner/core/logical_plan_builder.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index 4538aed36c28c..700a3a7b3af61 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -608,12 +608,12 @@ func (b *PlanBuilder) buildProjection(p LogicalPlan, fields []*ast.SelectField, oldLen++ } - hasWindow := ast.HasWindowFlag(field.Expr) + isWindowFuncField := ast.HasWindowFlag(field.Expr) // When `considerWindow` is false, we will only build fields for non-window functions, so we add fake placeholders. // When `considerWindow` is true, all the non-window fields have been built, so we just use the schema columns. - if (considerWindow && !hasWindow) || (!considerWindow && hasWindow) { + if (considerWindow && !isWindowFuncField) || (!considerWindow && isWindowFuncField) { var expr expression.Expression - if hasWindow { + if isWindowFuncField { expr = expression.Zero } else { expr = p.Schema().Columns[i] From b3ad3d98f2f7a467960685776be2e44d2f698d0c Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Thu, 27 Dec 2018 15:03:38 +0800 Subject: [PATCH 08/17] address comments --- planner/core/logical_plan_builder.go | 76 ++++++++++++++++------------ planner/core/logical_plan_test.go | 4 +- 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index 700a3a7b3af61..965c95e1418c4 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -2550,26 +2550,37 @@ func (b *PlanBuilder) buildDelete(delete *ast.DeleteStmt) (Plan, error) { return del, nil } -func (b *PlanBuilder) buildProjectionForWindow(p LogicalPlan, byItems []*ast.ByItem, aggMap map[*ast.AggregateFuncExpr]int) (LogicalPlan, []property.Item, error) { +func (b *PlanBuilder) buildProjectionForWindow(p LogicalPlan, expr *ast.WindowFuncExpr, aggMap map[*ast.AggregateFuncExpr]int) (LogicalPlan, []property.Item, []expression.Expression, error) { b.optFlag |= flagEliminateProjection - exprs := make([]property.Item, 0, len(byItems)) - proj := LogicalProjection{Exprs: make([]expression.Expression, 0, len(p.Schema().Columns)+len(byItems))}.Init(b.ctx) - schema := expression.NewSchema(make([]*expression.Column, 0, len(p.Schema().Columns)+len(byItems))...) + + var items []*ast.ByItem + spec := expr.Spec + if spec.PartitionBy != nil { + items = append(items, spec.PartitionBy.Items...) + } + if spec.OrderBy != nil { + items = append(items, spec.OrderBy.Items...) + } + projLen := len(p.Schema().Columns) + len(items) + len(expr.Args) + proj := LogicalProjection{Exprs: make([]expression.Expression, 0, projLen)}.Init(b.ctx) + schema := expression.NewSchema(make([]*expression.Column, 0, projLen)...) for _, col := range p.Schema().Columns { proj.Exprs = append(proj.Exprs, col) schema.Append(col) } + transformer := &itemTransformer{} - for _, item := range byItems { + propertyItems := make([]property.Item, 0, len(items)) + for _, item := range items { newExpr, _ := item.Expr.Accept(transformer) item.Expr = newExpr.(ast.ExprNode) it, np, err := b.rewrite(item.Expr, p, aggMap, true) if err != nil { - return nil, nil, err + return nil, nil, nil, err } p = np if col, ok := it.(*expression.Column); ok { - exprs = append(exprs, property.Item{Col: col, Desc: item.Desc}) + propertyItems = append(propertyItems, property.Item{Col: col, Desc: item.Desc}) continue } proj.Exprs = append(proj.Exprs, it) @@ -2579,40 +2590,39 @@ func (b *PlanBuilder) buildProjectionForWindow(p LogicalPlan, byItems []*ast.ByI RetType: it.GetType(), } schema.Append(col) - exprs = append(exprs, property.Item{Col: col, Desc: item.Desc}) - } - proj.SetSchema(schema) - proj.SetChildren(p) - return proj, exprs, nil -} - -func (b *PlanBuilder) buildWindowFunction(p LogicalPlan, expr *ast.WindowFuncExpr, aggMap map[*ast.AggregateFuncExpr]int) (*LogicalWindow, error) { - spec := expr.Spec - // Add sort for partition by and order by - var items []*ast.ByItem - if spec.PartitionBy != nil { - items = append(items, spec.PartitionBy.Items...) - } - if spec.OrderBy != nil { - items = append(items, spec.OrderBy.Items...) - } - var byItems []property.Item - if len(items) > 0 { - var err error - p, byItems, err = b.buildProjectionForWindow(p, items, aggMap) - if err != nil { - return nil, err - } + propertyItems = append(propertyItems, property.Item{Col: col, Desc: item.Desc}) } newArgList := make([]expression.Expression, 0, len(expr.Args)) for _, arg := range expr.Args { newArg, np, err := b.rewrite(arg, p, aggMap, true) if err != nil { - return nil, err + return nil, nil, nil, err } p = np - newArgList = append(newArgList, newArg) + if col, ok := newArg.(*expression.Column); ok { + newArgList = append(newArgList, col) + continue + } + proj.Exprs = append(proj.Exprs, newArg) + col := &expression.Column{ + ColName: model.NewCIStr(fmt.Sprintf("%d_proj_window_%d", p.ID(), schema.Len())), + UniqueID: b.ctx.GetSessionVars().AllocPlanColumnID(), + RetType: newArg.GetType(), + } + schema.Append(col) + newArgList = append(newArgList, col) + } + + proj.SetSchema(schema) + proj.SetChildren(p) + return proj, propertyItems, newArgList, nil +} + +func (b *PlanBuilder) buildWindowFunction(p LogicalPlan, expr *ast.WindowFuncExpr, aggMap map[*ast.AggregateFuncExpr]int) (*LogicalWindow, error) { + p, byItems, newArgList, err := b.buildProjectionForWindow(p, expr, aggMap) + if err != nil { + return nil, err } desc := aggregation.NewWindowFuncDesc(b.ctx, expr.F, newArgList) diff --git a/planner/core/logical_plan_test.go b/planner/core/logical_plan_test.go index ade57011c52ec..d2d151579d92a 100644 --- a/planner/core/logical_plan_test.go +++ b/planner/core/logical_plan_test.go @@ -1917,10 +1917,10 @@ func (s *testPlanSuite) TestWindowFunction(c *C) { }, { sql: "select a, avg(a) over(partition by b) from t", - result: "TableReader(Table(t))->Sort->WindowFunc(avg(test.t.a))->Projection", + result: "TableReader(Table(t))->Sort->WindowFunc(avg(2_proj_window_3))->Projection", }, { - sql: "select a, avg(a) over(partition by (a+1)) from t", + sql: "select a, avg(a+1) over(partition by (a+1)) from t", result: "TableReader(Table(t))->Projection->Sort->WindowFunc(avg(test.t.a))->Projection", }, { From 7e3e545d03204506377e75c6f400a73da17592c0 Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Thu, 27 Dec 2018 16:27:56 +0800 Subject: [PATCH 09/17] fix unit test --- planner/core/logical_plan_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/planner/core/logical_plan_test.go b/planner/core/logical_plan_test.go index d2d151579d92a..f1eba9fae7956 100644 --- a/planner/core/logical_plan_test.go +++ b/planner/core/logical_plan_test.go @@ -1917,11 +1917,11 @@ func (s *testPlanSuite) TestWindowFunction(c *C) { }, { sql: "select a, avg(a) over(partition by b) from t", - result: "TableReader(Table(t))->Sort->WindowFunc(avg(2_proj_window_3))->Projection", + result: "TableReader(Table(t))->Sort->WindowFunc(avg(test.t.a))->Projection", }, { sql: "select a, avg(a+1) over(partition by (a+1)) from t", - result: "TableReader(Table(t))->Projection->Sort->WindowFunc(avg(test.t.a))->Projection", + result: "TableReader(Table(t))->Projection->Sort->WindowFunc(avg(2_proj_window_3))->Projection", }, { sql: "select a, avg(a) over(order by a asc, b desc) from t order by a asc, b desc", From aec7a2cbafadb797f7149e7d662c7b98488152a9 Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Sat, 29 Dec 2018 15:08:57 +0800 Subject: [PATCH 10/17] address comments --- planner/core/logical_plan_builder.go | 4 ++-- planner/core/stringer.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index 965c95e1418c4..96daf1df61f56 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -2620,12 +2620,12 @@ func (b *PlanBuilder) buildProjectionForWindow(p LogicalPlan, expr *ast.WindowFu } func (b *PlanBuilder) buildWindowFunction(p LogicalPlan, expr *ast.WindowFuncExpr, aggMap map[*ast.AggregateFuncExpr]int) (*LogicalWindow, error) { - p, byItems, newArgList, err := b.buildProjectionForWindow(p, expr, aggMap) + p, byItems, args, err := b.buildProjectionForWindow(p, expr, aggMap) if err != nil { return nil, err } - desc := aggregation.NewWindowFuncDesc(b.ctx, expr.F, newArgList) + desc := aggregation.NewWindowFuncDesc(b.ctx, expr.F, args) window := LogicalWindow{ WindowFuncDesc: desc, ByItems: byItems, diff --git a/planner/core/stringer.go b/planner/core/stringer.go index 86267c3aced98..62459b66d4524 100644 --- a/planner/core/stringer.go +++ b/planner/core/stringer.go @@ -221,9 +221,9 @@ func toString(in Plan, strs []string, idxs []int) ([]string, []int) { str = fmt.Sprintf("%s->Insert", ToString(x.SelectPlan)) } case *LogicalWindow: - str = fmt.Sprintf("WindowFunc(%s)", x.WindowFuncDesc.String()) + str = fmt.Sprintf("Window(%s)", x.WindowFuncDesc.String()) case *PhysicalWindow: - str = fmt.Sprintf("WindowFunc(%s)", x.WindowFuncDesc.String()) + str = fmt.Sprintf("Window(%s)", x.WindowFuncDesc.String()) default: str = fmt.Sprintf("%T", in) } From b9b336671cbdc399ae2810ee88083665d4a29447 Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Wed, 2 Jan 2019 13:24:07 +0800 Subject: [PATCH 11/17] add comment for build projection --- planner/core/logical_plan_builder.go | 2 ++ planner/core/logical_plan_test.go | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index 96daf1df61f56..f0d59273be653 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -2550,6 +2550,8 @@ func (b *PlanBuilder) buildDelete(delete *ast.DeleteStmt) (Plan, error) { return del, nil } +// buildProjectionForWindow builds the projection for expressions in the window specification that is not an column, +// so after the projection, window functions only needs to deal with columns. func (b *PlanBuilder) buildProjectionForWindow(p LogicalPlan, expr *ast.WindowFuncExpr, aggMap map[*ast.AggregateFuncExpr]int) (LogicalPlan, []property.Item, []expression.Expression, error) { b.optFlag |= flagEliminateProjection diff --git a/planner/core/logical_plan_test.go b/planner/core/logical_plan_test.go index f1eba9fae7956..537ebf678085c 100644 --- a/planner/core/logical_plan_test.go +++ b/planner/core/logical_plan_test.go @@ -1913,23 +1913,23 @@ func (s *testPlanSuite) TestWindowFunction(c *C) { }{ { sql: "select a, avg(a) over(partition by a) from t", - result: "TableReader(Table(t))->WindowFunc(avg(test.t.a))->Projection", + result: "TableReader(Table(t))->Window(avg(test.t.a))->Projection", }, { sql: "select a, avg(a) over(partition by b) from t", - result: "TableReader(Table(t))->Sort->WindowFunc(avg(test.t.a))->Projection", + result: "TableReader(Table(t))->Sort->Window(avg(test.t.a))->Projection", }, { sql: "select a, avg(a+1) over(partition by (a+1)) from t", - result: "TableReader(Table(t))->Projection->Sort->WindowFunc(avg(2_proj_window_3))->Projection", + result: "TableReader(Table(t))->Projection->Sort->Window(avg(2_proj_window_3))->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->WindowFunc(avg(test.t.a))->Projection", + result: "TableReader(Table(t))->Sort->Window(avg(test.t.a))->Projection", }, { sql: "select a, b as a, avg(a) over(partition by a) from t", - result: "TableReader(Table(t))->WindowFunc(avg(test.t.a))->Projection", + result: "TableReader(Table(t))->Window(avg(test.t.a))->Projection", }, { sql: "select a, b as z, sum(z) over() from t", @@ -1937,27 +1937,27 @@ func (s *testPlanSuite) TestWindowFunction(c *C) { }, { sql: "select a, b as z from t order by (sum(z) over())", - result: "TableReader(Table(t))->WindowFunc(sum(test.t.z))->Sort->Projection", + result: "TableReader(Table(t))->Window(sum(test.t.z))->Sort->Projection", }, { sql: "select sum(avg(a)) over() from t", - result: "TableReader(Table(t)->StreamAgg)->StreamAgg->WindowFunc(sum(sel_agg_2))->Projection", + result: "TableReader(Table(t)->StreamAgg)->StreamAgg->Window(sum(sel_agg_2))->Projection", }, { sql: "select b from t order by(sum(a) over())", - result: "TableReader(Table(t))->WindowFunc(sum(test.t.a))->Sort->Projection", + result: "TableReader(Table(t))->Window(sum(test.t.a))->Sort->Projection", }, { sql: "select b from t order by(sum(a) over(partition by a))", - result: "TableReader(Table(t))->WindowFunc(sum(test.t.a))->Sort->Projection", + result: "TableReader(Table(t))->Window(sum(test.t.a))->Sort->Projection", }, { sql: "select b from t order by(sum(avg(a)) over())", - result: "TableReader(Table(t)->StreamAgg)->StreamAgg->WindowFunc(sum(sel_agg_2))->Sort->Projection", + result: "TableReader(Table(t)->StreamAgg)->StreamAgg->Window(sum(sel_agg_2))->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)]))->WindowFunc(sum(tt.a))->MaxOneRow->Sel([w])}->Projection", + result: "Apply{TableReader(Table(t))->TableReader(Table(t)->Sel([gt(tt.a, test.t.a)]))->Window(sum(tt.a))->MaxOneRow->Sel([w])}->Projection", }, { sql: "select avg(a) over() as w from t having w > 1", From dcf89811c107986dc9963b77698b34c052717b7a Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Wed, 2 Jan 2019 20:07:13 +0800 Subject: [PATCH 12/17] add more comments --- planner/core/logical_plan_builder.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index f0d59273be653..f21d23f9e7345 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -609,7 +609,10 @@ func (b *PlanBuilder) buildProjection(p LogicalPlan, fields []*ast.SelectField, } isWindowFuncField := ast.HasWindowFlag(field.Expr) + // Although window functions occurs in the select fields, but it has to be processed after having clause. + // So when we build the projection for select fields, we need to skip the window function. // When `considerWindow` is false, we will only build fields for non-window functions, so we add fake placeholders. + // for window functions. These fake placeholders will be erased in column pruning. // When `considerWindow` is true, all the non-window fields have been built, so we just use the schema columns. if (considerWindow && !isWindowFuncField) || (!considerWindow && isWindowFuncField) { var expr expression.Expression From b2d293f905d5d168df31d1b3252fce700b900062 Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Thu, 3 Jan 2019 12:42:50 +0800 Subject: [PATCH 13/17] address comments --- planner/core/logical_plan_builder.go | 3 ++- planner/core/rule_eliminate_projection.go | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index f21d23f9e7345..832670a78a7ae 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -1863,7 +1863,8 @@ func (b *PlanBuilder) buildSelect(sel *ast.SelectStmt) (p LogicalPlan, err error } var oldLen int - // `considerWindow` is false now because we can only process window functions after having clause. + // According to https://dev.mysql.com/doc/refman/8.0/en/window-functions-usage.html, + // we can only process window functions after having clause., so `considerWindow` is false now. p, oldLen, err = b.buildProjection(p, sel.Fields.Fields, totalMap, false) if err != nil { return nil, errors.Trace(err) diff --git a/planner/core/rule_eliminate_projection.go b/planner/core/rule_eliminate_projection.go index 3e70bdb998327..b0444721a21f3 100644 --- a/planner/core/rule_eliminate_projection.go +++ b/planner/core/rule_eliminate_projection.go @@ -204,3 +204,12 @@ func (lt *LogicalTopN) replaceExprColumns(replace map[string]*expression.Column) resolveExprAndReplace(byItem.Expr, replace) } } + +func (p *LogicalWindow) replaceExprColumns(replace map[string]*expression.Column) { + for _, arg := range p.WindowFuncDesc.Args { + resolveExprAndReplace(arg, replace) + } + for _, item := range p.ByItems { + resolveColumnAndReplace(item.Col, replace) + } +} From 87e50abfaa66751cab25f41e0b62c29c85a72003 Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Thu, 3 Jan 2019 12:45:05 +0800 Subject: [PATCH 14/17] remove dot --- planner/core/logical_plan_builder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index 832670a78a7ae..0db95663a5e95 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -1864,7 +1864,7 @@ func (b *PlanBuilder) buildSelect(sel *ast.SelectStmt) (p LogicalPlan, err error var oldLen int // According to https://dev.mysql.com/doc/refman/8.0/en/window-functions-usage.html, - // we can only process window functions after having clause., so `considerWindow` is false now. + // we can only process window functions after having clause, so `considerWindow` is false now. p, oldLen, err = b.buildProjection(p, sel.Fields.Fields, totalMap, false) if err != nil { return nil, errors.Trace(err) From 2fd62baaaa2f18a57073dcbb96ef8eece23b10d0 Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Thu, 3 Jan 2019 13:36:04 +0800 Subject: [PATCH 15/17] address comments --- planner/core/rule_eliminate_projection.go | 2 ++ planner/core/rule_predicate_push_down.go | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/planner/core/rule_eliminate_projection.go b/planner/core/rule_eliminate_projection.go index b0444721a21f3..0f4f8f59a0941 100644 --- a/planner/core/rule_eliminate_projection.go +++ b/planner/core/rule_eliminate_projection.go @@ -117,6 +117,8 @@ func (pe *projectionEliminater) eliminate(p LogicalPlan, replace map[string]*exp childFlag = false } else if _, isAgg := p.(*LogicalAggregation); isAgg || isProj { childFlag = true + } else if _, isWindow := p.(*LogicalWindow); isWindow || isProj { + childFlag = true } for i, child := range p.Children() { p.Children()[i] = pe.eliminate(child, replace, childFlag) diff --git a/planner/core/rule_predicate_push_down.go b/planner/core/rule_predicate_push_down.go index bf352289da908..1523ebeaf136f 100644 --- a/planner/core/rule_predicate_push_down.go +++ b/planner/core/rule_predicate_push_down.go @@ -465,3 +465,10 @@ func (p *LogicalJoin) outerJoinPropConst(predicates []expression.Expression) []e p.attachOnConds(joinConds) return predicates } + +// PredicatePushDown implements LogicalPlan PredicatePushDown interface. +func (p *LogicalWindow) PredicatePushDown(predicates []expression.Expression) ([]expression.Expression, LogicalPlan) { + // Window function forbids any condition to push down. + p.baseLogicalPlan.PredicatePushDown(nil) + return predicates, p +} From 58de4fc2bb9b6ccced5eddcc857f836824ba0026 Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Thu, 3 Jan 2019 15:18:56 +0800 Subject: [PATCH 16/17] address comment --- planner/core/errors.go | 98 ++++++++++++----------- planner/core/rule_eliminate_projection.go | 2 +- 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/planner/core/errors.go b/planner/core/errors.go index 0541b4b8d7bfc..b49a3c9719328 100644 --- a/planner/core/errors.go +++ b/planner/core/errors.go @@ -66,60 +66,62 @@ var ( ErrSchemaChanged = terror.ClassOptimizer.New(codeSchemaChanged, "Schema has changed") ErrTablenameNotAllowedHere = terror.ClassOptimizer.New(codeTablenameNotAllowedHere, "Table '%s' from one of the %ss cannot be used in %s") - ErrWrongUsage = terror.ClassOptimizer.New(codeWrongUsage, mysql.MySQLErrName[mysql.ErrWrongUsage]) - ErrAmbiguous = terror.ClassOptimizer.New(codeAmbiguous, mysql.MySQLErrName[mysql.ErrNonUniq]) - ErrUnknown = terror.ClassOptimizer.New(codeUnknown, mysql.MySQLErrName[mysql.ErrUnknown]) - ErrUnknownColumn = terror.ClassOptimizer.New(codeUnknownColumn, mysql.MySQLErrName[mysql.ErrBadField]) - ErrUnknownTable = terror.ClassOptimizer.New(codeUnknownTable, mysql.MySQLErrName[mysql.ErrUnknownTable]) - ErrWrongArguments = terror.ClassOptimizer.New(codeWrongArguments, mysql.MySQLErrName[mysql.ErrWrongArguments]) - ErrWrongNumberOfColumnsInSelect = terror.ClassOptimizer.New(codeWrongNumberOfColumnsInSelect, mysql.MySQLErrName[mysql.ErrWrongNumberOfColumnsInSelect]) - ErrBadGeneratedColumn = terror.ClassOptimizer.New(codeBadGeneratedColumn, mysql.MySQLErrName[mysql.ErrBadGeneratedColumn]) - ErrFieldNotInGroupBy = terror.ClassOptimizer.New(codeFieldNotInGroupBy, mysql.MySQLErrName[mysql.ErrFieldNotInGroupBy]) - ErrBadTable = terror.ClassOptimizer.New(codeBadTable, mysql.MySQLErrName[mysql.ErrBadTable]) - ErrKeyDoesNotExist = terror.ClassOptimizer.New(codeKeyDoesNotExist, mysql.MySQLErrName[mysql.ErrKeyDoesNotExist]) - ErrOperandColumns = terror.ClassOptimizer.New(codeOperandColumns, mysql.MySQLErrName[mysql.ErrOperandColumns]) - ErrInvalidWildCard = terror.ClassOptimizer.New(codeInvalidWildCard, "Wildcard fields without any table name appears in wrong place") - ErrInvalidGroupFuncUse = terror.ClassOptimizer.New(codeInvalidGroupFuncUse, mysql.MySQLErrName[mysql.ErrInvalidGroupFuncUse]) - ErrIllegalReference = terror.ClassOptimizer.New(codeIllegalReference, mysql.MySQLErrName[mysql.ErrIllegalReference]) - ErrNoDB = terror.ClassOptimizer.New(codeNoDB, mysql.MySQLErrName[mysql.ErrNoDB]) - ErrUnknownExplainFormat = terror.ClassOptimizer.New(codeUnknownExplainFormat, mysql.MySQLErrName[mysql.ErrUnknownExplainFormat]) - ErrWrongGroupField = terror.ClassOptimizer.New(codeWrongGroupField, mysql.MySQLErrName[mysql.ErrWrongGroupField]) - ErrDupFieldName = terror.ClassOptimizer.New(codeDupFieldName, mysql.MySQLErrName[mysql.ErrDupFieldName]) - ErrNonUpdatableTable = terror.ClassOptimizer.New(codeNonUpdatableTable, mysql.MySQLErrName[mysql.ErrNonUpdatableTable]) - ErrInternal = terror.ClassOptimizer.New(codeInternal, mysql.MySQLErrName[mysql.ErrInternal]) - ErrMixOfGroupFuncAndFields = terror.ClassOptimizer.New(codeMixOfGroupFuncAndFields, "In aggregated query without GROUP BY, expression #%d of SELECT list contains nonaggregated column '%s'; this is incompatible with sql_mode=only_full_group_by") - ErrNonUniqTable = terror.ClassOptimizer.New(codeNonUniqTable, mysql.MySQLErrName[mysql.ErrNonuniqTable]) - ErrWrongValueCountOnRow = terror.ClassOptimizer.New(mysql.ErrWrongValueCountOnRow, mysql.MySQLErrName[mysql.ErrWrongValueCountOnRow]) - ErrViewInvalid = terror.ClassOptimizer.New(mysql.ErrViewInvalid, mysql.MySQLErrName[mysql.ErrViewInvalid]) + ErrWrongUsage = terror.ClassOptimizer.New(codeWrongUsage, mysql.MySQLErrName[mysql.ErrWrongUsage]) + ErrAmbiguous = terror.ClassOptimizer.New(codeAmbiguous, mysql.MySQLErrName[mysql.ErrNonUniq]) + ErrUnknown = terror.ClassOptimizer.New(codeUnknown, mysql.MySQLErrName[mysql.ErrUnknown]) + ErrUnknownColumn = terror.ClassOptimizer.New(codeUnknownColumn, mysql.MySQLErrName[mysql.ErrBadField]) + ErrUnknownTable = terror.ClassOptimizer.New(codeUnknownTable, mysql.MySQLErrName[mysql.ErrUnknownTable]) + ErrWrongArguments = terror.ClassOptimizer.New(codeWrongArguments, mysql.MySQLErrName[mysql.ErrWrongArguments]) + ErrWrongNumberOfColumnsInSelect = terror.ClassOptimizer.New(codeWrongNumberOfColumnsInSelect, mysql.MySQLErrName[mysql.ErrWrongNumberOfColumnsInSelect]) + ErrBadGeneratedColumn = terror.ClassOptimizer.New(codeBadGeneratedColumn, mysql.MySQLErrName[mysql.ErrBadGeneratedColumn]) + ErrFieldNotInGroupBy = terror.ClassOptimizer.New(codeFieldNotInGroupBy, mysql.MySQLErrName[mysql.ErrFieldNotInGroupBy]) + ErrBadTable = terror.ClassOptimizer.New(codeBadTable, mysql.MySQLErrName[mysql.ErrBadTable]) + ErrKeyDoesNotExist = terror.ClassOptimizer.New(codeKeyDoesNotExist, mysql.MySQLErrName[mysql.ErrKeyDoesNotExist]) + ErrOperandColumns = terror.ClassOptimizer.New(codeOperandColumns, mysql.MySQLErrName[mysql.ErrOperandColumns]) + ErrInvalidWildCard = terror.ClassOptimizer.New(codeInvalidWildCard, "Wildcard fields without any table name appears in wrong place") + ErrInvalidGroupFuncUse = terror.ClassOptimizer.New(codeInvalidGroupFuncUse, mysql.MySQLErrName[mysql.ErrInvalidGroupFuncUse]) + ErrIllegalReference = terror.ClassOptimizer.New(codeIllegalReference, mysql.MySQLErrName[mysql.ErrIllegalReference]) + ErrNoDB = terror.ClassOptimizer.New(codeNoDB, mysql.MySQLErrName[mysql.ErrNoDB]) + ErrUnknownExplainFormat = terror.ClassOptimizer.New(codeUnknownExplainFormat, mysql.MySQLErrName[mysql.ErrUnknownExplainFormat]) + ErrWrongGroupField = terror.ClassOptimizer.New(codeWrongGroupField, mysql.MySQLErrName[mysql.ErrWrongGroupField]) + ErrDupFieldName = terror.ClassOptimizer.New(codeDupFieldName, mysql.MySQLErrName[mysql.ErrDupFieldName]) + ErrNonUpdatableTable = terror.ClassOptimizer.New(codeNonUpdatableTable, mysql.MySQLErrName[mysql.ErrNonUpdatableTable]) + ErrInternal = terror.ClassOptimizer.New(codeInternal, mysql.MySQLErrName[mysql.ErrInternal]) + ErrMixOfGroupFuncAndFields = terror.ClassOptimizer.New(codeMixOfGroupFuncAndFields, "In aggregated query without GROUP BY, expression #%d of SELECT list contains nonaggregated column '%s'; this is incompatible with sql_mode=only_full_group_by") + ErrNonUniqTable = terror.ClassOptimizer.New(codeNonUniqTable, mysql.MySQLErrName[mysql.ErrNonuniqTable]) + ErrWrongValueCountOnRow = terror.ClassOptimizer.New(mysql.ErrWrongValueCountOnRow, mysql.MySQLErrName[mysql.ErrWrongValueCountOnRow]) + ErrViewInvalid = terror.ClassOptimizer.New(mysql.ErrViewInvalid, mysql.MySQLErrName[mysql.ErrViewInvalid]) + ErrWindowInvalidWindowFuncUse = terror.ClassOptimizer.New(codeWindowInvalidWindowFuncUse, mysql.MySQLErrName[mysql.ErrWindowInvalidWindowFuncUse]) ErrWindowInvalidWindowFuncAliasUse = terror.ClassOptimizer.New(codeWindowInvalidWindowFuncAliasUse, mysql.MySQLErrName[mysql.ErrWindowInvalidWindowFuncAliasUse]) ) func init() { mysqlErrCodeMap := map[terror.ErrCode]uint16{ - codeWrongUsage: mysql.ErrWrongUsage, - codeAmbiguous: mysql.ErrNonUniq, - codeUnknownColumn: mysql.ErrBadField, - codeUnknownTable: mysql.ErrBadTable, - codeWrongArguments: mysql.ErrWrongArguments, - codeBadGeneratedColumn: mysql.ErrBadGeneratedColumn, - codeFieldNotInGroupBy: mysql.ErrFieldNotInGroupBy, - codeBadTable: mysql.ErrBadTable, - codeKeyDoesNotExist: mysql.ErrKeyDoesNotExist, - codeOperandColumns: mysql.ErrOperandColumns, - codeInvalidWildCard: mysql.ErrParse, - codeInvalidGroupFuncUse: mysql.ErrInvalidGroupFuncUse, - codeIllegalReference: mysql.ErrIllegalReference, - codeNoDB: mysql.ErrNoDB, - codeUnknownExplainFormat: mysql.ErrUnknownExplainFormat, - codeWrongGroupField: mysql.ErrWrongGroupField, - codeDupFieldName: mysql.ErrDupFieldName, - codeNonUpdatableTable: mysql.ErrUnknownTable, - codeInternal: mysql.ErrInternal, - codeMixOfGroupFuncAndFields: mysql.ErrMixOfGroupFuncAndFields, - codeNonUniqTable: mysql.ErrNonuniqTable, - codeWrongNumberOfColumnsInSelect: mysql.ErrWrongNumberOfColumnsInSelect, - codeWrongValueCountOnRow: mysql.ErrWrongValueCountOnRow, + codeWrongUsage: mysql.ErrWrongUsage, + codeAmbiguous: mysql.ErrNonUniq, + codeUnknownColumn: mysql.ErrBadField, + codeUnknownTable: mysql.ErrBadTable, + codeWrongArguments: mysql.ErrWrongArguments, + codeBadGeneratedColumn: mysql.ErrBadGeneratedColumn, + codeFieldNotInGroupBy: mysql.ErrFieldNotInGroupBy, + codeBadTable: mysql.ErrBadTable, + codeKeyDoesNotExist: mysql.ErrKeyDoesNotExist, + codeOperandColumns: mysql.ErrOperandColumns, + codeInvalidWildCard: mysql.ErrParse, + codeInvalidGroupFuncUse: mysql.ErrInvalidGroupFuncUse, + codeIllegalReference: mysql.ErrIllegalReference, + codeNoDB: mysql.ErrNoDB, + codeUnknownExplainFormat: mysql.ErrUnknownExplainFormat, + codeWrongGroupField: mysql.ErrWrongGroupField, + codeDupFieldName: mysql.ErrDupFieldName, + codeNonUpdatableTable: mysql.ErrUnknownTable, + codeInternal: mysql.ErrInternal, + codeMixOfGroupFuncAndFields: mysql.ErrMixOfGroupFuncAndFields, + codeNonUniqTable: mysql.ErrNonuniqTable, + codeWrongNumberOfColumnsInSelect: mysql.ErrWrongNumberOfColumnsInSelect, + codeWrongValueCountOnRow: mysql.ErrWrongValueCountOnRow, + codeWindowInvalidWindowFuncUse: mysql.ErrWindowInvalidWindowFuncUse, codeWindowInvalidWindowFuncAliasUse: mysql.ErrWindowInvalidWindowFuncAliasUse, } diff --git a/planner/core/rule_eliminate_projection.go b/planner/core/rule_eliminate_projection.go index 0f4f8f59a0941..597ce50a9e8d6 100644 --- a/planner/core/rule_eliminate_projection.go +++ b/planner/core/rule_eliminate_projection.go @@ -117,7 +117,7 @@ func (pe *projectionEliminater) eliminate(p LogicalPlan, replace map[string]*exp childFlag = false } else if _, isAgg := p.(*LogicalAggregation); isAgg || isProj { childFlag = true - } else if _, isWindow := p.(*LogicalWindow); isWindow || isProj { + } else if _, isWindow := p.(*LogicalWindow); isWindow { childFlag = true } for i, child := range p.Children() { From 36c5993ba081eada4e69744b72da12563085aba0 Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Thu, 3 Jan 2019 16:01:01 +0800 Subject: [PATCH 17/17] update parser --- go.mod | 4 +--- go.sum | 5 ++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 233e593fc2961..f540edad820c6 100644 --- a/go.mod +++ b/go.mod @@ -48,7 +48,7 @@ require ( github.com/pingcap/gofail v0.0.0-20181217135706-6a951c1e42c3 github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e github.com/pingcap/kvproto v0.0.0-20181203065228-c14302da291c - github.com/pingcap/parser v0.0.0-20181225032741-ff56f7f11ed6 + github.com/pingcap/parser v0.0.0-20190103075927-c065c7404641 github.com/pingcap/pd v2.1.0-rc.4+incompatible github.com/pingcap/tidb-tools v2.1.1-0.20181218072513-b2235d442b06+incompatible github.com/pingcap/tipb v0.0.0-20181012112600-11e33c750323 @@ -86,5 +86,3 @@ require ( sourcegraph.com/sourcegraph/appdash v0.0.0-20180531100431-4c381bd170b4 sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67 ) - -replace github.com/pingcap/parser => github.com/lamxTyler/parser v0.0.0-20181226064458-13cec0b9d426 diff --git a/go.sum b/go.sum index c6977fa3d8415..2b387fb00b4e6 100644 --- a/go.sum +++ b/go.sum @@ -81,7 +81,6 @@ github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5 h1:2U0HzY8BJ8hVwDK github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/lamxTyler/parser v0.0.0-20181226064458-13cec0b9d426/go.mod h1:6c1rwSy9dUuNebYdr1IMI4+/sT3/Q65MXP2UCg7/vJI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/montanaflynn/stats v0.0.0-20180911141734-db72e6cae808 h1:pmpDGKLw4n82EtrNiLqB+xSz/JQwFOaZuMALYUHwX5s= @@ -111,8 +110,8 @@ github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e h1:P73/4dPCL96rG github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e/go.mod h1:O17XtbryoCJhkKGbT62+L2OlrniwqiGLSqrmdHCMzZw= github.com/pingcap/kvproto v0.0.0-20181203065228-c14302da291c h1:Qf5St5XGwKgKQLar9lEXoeO0hJMVaFBj3JqvFguWtVg= github.com/pingcap/kvproto v0.0.0-20181203065228-c14302da291c/go.mod h1:Ja9XPjot9q4/3JyCZodnWDGNXt4pKemhIYCvVJM7P24= -github.com/pingcap/parser v0.0.0-20181225032741-ff56f7f11ed6 h1:ooapyJxH6uSHNvpYjPOggHtd2dzLKwSvYLVzs3OjoM0= -github.com/pingcap/parser v0.0.0-20181225032741-ff56f7f11ed6/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA= +github.com/pingcap/parser v0.0.0-20190103075927-c065c7404641 h1:KTGU8kr2wY+FRiHHs8I5lp385b+OzYnbOr3/tPVw7mU= +github.com/pingcap/parser v0.0.0-20190103075927-c065c7404641/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA= github.com/pingcap/pd v2.1.0-rc.4+incompatible h1:/buwGk04aHO5odk/+O8ZOXGs4qkUjYTJ2UpCJXna8NE= github.com/pingcap/pd v2.1.0-rc.4+incompatible/go.mod h1:nD3+EoYes4+aNNODO99ES59V83MZSI+dFbhyr667a0E= github.com/pingcap/tidb-tools v2.1.1-0.20181218072513-b2235d442b06+incompatible h1:Bsd+NHosPVowEGB3BCx+2d8wUQGDTXSSC5ljeNS6cXo=