Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

planner: support window function #8630

Merged
merged 29 commits into from
Jan 3, 2019
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1aaf513
planner: support window function
alivxxx Dec 7, 2018
d8ebf09
Merge branch 'master' of github.com:pingcap/tidb into window
alivxxx Dec 10, 2018
52d7513
Merge branch 'master' of github.com:pingcap/tidb into window
alivxxx Dec 10, 2018
db59bed
Merge branch 'master' of github.com:pingcap/tidb into window
alivxxx Dec 10, 2018
aaa2adf
Merge branch 'master' into window
zz-jason Dec 10, 2018
6244626
address comments
alivxxx Dec 11, 2018
1f077b8
Merge branch 'master' of github.com:pingcap/tidb into window
alivxxx Dec 11, 2018
f120ab9
Merge branch 'window' of github.com:lamxTyler/tidb into window
alivxxx Dec 11, 2018
2b8d890
use latest parser
alivxxx Dec 11, 2018
3095b2d
Merge branch 'master' of github.com:pingcap/tidb into window
alivxxx Dec 18, 2018
aa59c92
address comments
alivxxx Dec 19, 2018
eddbb79
Merge branch 'master' of github.com:pingcap/tidb into window
alivxxx Dec 26, 2018
33e76f9
fix gofmt
alivxxx Dec 26, 2018
db13052
address comment
alivxxx Dec 26, 2018
ecf4ef8
address comments
alivxxx Dec 26, 2018
b3ad3d9
address comments
alivxxx Dec 27, 2018
7e3e545
fix unit test
alivxxx Dec 27, 2018
aec7a2c
address comments
alivxxx Dec 29, 2018
71bdea1
Merge branch 'master' into window
zz-jason Dec 29, 2018
b9b3366
add comment for build projection
alivxxx Jan 2, 2019
d047eb2
Merge branch 'window' of github.com:lamxTyler/tidb into window
alivxxx Jan 2, 2019
dcf8981
add more comments
alivxxx Jan 2, 2019
b2d293f
address comments
alivxxx Jan 3, 2019
87e50ab
remove dot
alivxxx Jan 3, 2019
2fd62ba
address comments
alivxxx Jan 3, 2019
58de4fc
address comment
alivxxx Jan 3, 2019
36c5993
update parser
alivxxx Jan 3, 2019
ccc13a4
Merge branch 'master' into window
alivxxx Jan 3, 2019
5eb3e5b
Merge branch 'master' into window
eurekaka Jan 3, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions expression/aggregation/window_func.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2018 PingCAP, Inc.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should it belong to aggregation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the package name to udf or something else?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to, window function is also a kind of aggregate function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But its behavior is quite different with normal aggregate function since it won't make the rows grouped into one though there's OVER clause.
I think they should be in the same level not superior-subordinate.

//
// 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)}
}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -86,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-20181226064458-13cec0b9d426
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,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-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=
Expand Down
103 changes: 55 additions & 48 deletions planner/core/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ const (
codeWrongNumberOfColumnsInSelect = mysql.ErrWrongNumberOfColumnsInSelect
codeWrongValueCountOnRow = mysql.ErrWrongValueCountOnRow
codeTablenameNotAllowedHere = mysql.ErrTablenameNotAllowedHere

codeWindowInvalidWindowFuncUse = mysql.ErrWindowInvalidWindowFuncUse
codeWindowInvalidWindowFuncAliasUse = mysql.ErrWindowInvalidWindowFuncAliasUse
)

// error definitions.
Expand All @@ -63,58 +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,
}
terror.ErrClassToMySQLCodes[terror.ClassOptimizer] = mysqlErrCodeMap
}
12 changes: 12 additions & 0 deletions planner/core/exhaust_physical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,18 @@ func (la *LogicalApply) exhaustPhysicalPlans(prop *property.PhysicalProperty) []
return []PhysicalPlan{apply}
}

func (p *LogicalWindow) exhaustPhysicalPlans(prop *property.PhysicalProperty) []PhysicalPlan {
childProperty := &property.PhysicalProperty{ExpectedCnt: math.MaxFloat64, Items: p.ByItems, Enforced: true}
if !prop.IsPrefix(childProperty) {
return nil
}
window := PhysicalWindow{
WindowFuncDesc: p.WindowFuncDesc,
}.Init(p.ctx, p.stats.ScaleByExpectCnt(prop.ExpectedCnt), childProperty)
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.")
Expand Down
6 changes: 6 additions & 0 deletions planner/core/explain.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,3 +297,9 @@ 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 *PhysicalWindow) ExplainInfo() string {
// TODO: Add explain info for partition by, order by and frame.
zz-jason marked this conversation as resolved.
Show resolved Hide resolved
return p.WindowFuncDesc.String()
zz-jason marked this conversation as resolved.
Show resolved Hide resolved
}
15 changes: 14 additions & 1 deletion planner/core/expression_rewriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
zz-jason marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
er.err = 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 {
Expand Down Expand Up @@ -751,7 +764,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)
Expand Down
16 changes: 16 additions & 0 deletions planner/core/initialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ const (
TypeTableReader = "TableReader"
// TypeIndexReader is the type of IndexReader.
TypeIndexReader = "IndexReader"
// TypeWindow is the type of Window.
TypeWindow = "Window"
)

// Init initializes LogicalAggregation.
Expand Down Expand Up @@ -231,6 +233,20 @@ func (p PhysicalMaxOneRow) Init(ctx sessionctx.Context, stats *property.StatsInf
return &p
}

// Init initializes LogicalWindow.
func (p LogicalWindow) Init(ctx sessionctx.Context) *LogicalWindow {
p.baseLogicalPlan = newBaseLogicalPlan(ctx, TypeWindow, &p)
return &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
}

// Init initializes Update.
func (p Update) Init(ctx sessionctx.Context) *Update {
p.basePlan = newBasePlan(ctx, TypeUpdate)
Expand Down
Loading