planner: classify logical optimizing rule interface and files. (#55226)
ref pingcap/tidb#51664, ref pingcap/tidb#52714
This commit is contained in:
@ -68,7 +68,6 @@ go_library(
|
||||
"rule_aggregation_elimination.go",
|
||||
"rule_aggregation_push_down.go",
|
||||
"rule_aggregation_skew_rewrite.go",
|
||||
"rule_build_key_info.go",
|
||||
"rule_collect_plan_stats.go",
|
||||
"rule_column_pruning.go",
|
||||
"rule_constant_propagation.go",
|
||||
@ -140,6 +139,7 @@ go_library(
|
||||
"//pkg/planner/core/metrics",
|
||||
"//pkg/planner/core/operator/baseimpl",
|
||||
"//pkg/planner/core/operator/logicalop",
|
||||
"//pkg/planner/core/rule",
|
||||
"//pkg/planner/core/rule/util",
|
||||
"//pkg/planner/funcdep",
|
||||
"//pkg/planner/property",
|
||||
|
||||
@ -6,6 +6,7 @@ go_library(
|
||||
"doc.go",
|
||||
"misc_base.go",
|
||||
"plan_base.go",
|
||||
"rule_base.go",
|
||||
"task_base.go",
|
||||
],
|
||||
importpath = "github.com/pingcap/tidb/pkg/planner/core/base",
|
||||
|
||||
33
pkg/planner/core/base/rule_base.go
Normal file
33
pkg/planner/core/base/rule_base.go
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2024 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,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pingcap/tidb/pkg/planner/util/optimizetrace"
|
||||
)
|
||||
|
||||
// LogicalOptRule means a logical optimizing rule, which contains de-correlate, ppd, column pruning, etc.
|
||||
type LogicalOptRule interface {
|
||||
// Optimize return parameters:
|
||||
// 1. base.LogicalPlan: The optimized base.LogicalPlan after rule is applied
|
||||
// 2. bool: Used to judge whether the plan is changed or not by logical rule.
|
||||
// If the plan is changed, it will return true.
|
||||
// The default value is false. It means that no interaction rule will be triggered.
|
||||
// 3. error: If there is error during the rule optimizer, it will be thrown
|
||||
Optimize(context.Context, LogicalPlan, *optimizetrace.LogicalOptimizeOp) (LogicalPlan, bool, error)
|
||||
Name() string
|
||||
}
|
||||
@ -29,6 +29,7 @@ import (
|
||||
"github.com/pingcap/tidb/pkg/planner/core/base"
|
||||
"github.com/pingcap/tidb/pkg/planner/core/cost"
|
||||
"github.com/pingcap/tidb/pkg/planner/core/operator/logicalop"
|
||||
ruleutil "github.com/pingcap/tidb/pkg/planner/core/rule/util"
|
||||
fd "github.com/pingcap/tidb/pkg/planner/funcdep"
|
||||
"github.com/pingcap/tidb/pkg/planner/property"
|
||||
"github.com/pingcap/tidb/pkg/planner/util"
|
||||
@ -257,7 +258,7 @@ func (ds *DataSource) BuildKeyInfo(selfSchema *expression.Schema, _ []*expressio
|
||||
} else if index.State != model.StatePublic {
|
||||
continue
|
||||
}
|
||||
if uniqueKey, newKey := checkIndexCanBeKey(index, ds.Columns, selfSchema); newKey != nil {
|
||||
if uniqueKey, newKey := ruleutil.CheckIndexCanBeKey(index, ds.Columns, selfSchema); newKey != nil {
|
||||
selfSchema.Keys = append(selfSchema.Keys, newKey)
|
||||
} else if uniqueKey != nil {
|
||||
selfSchema.UniqueKeys = append(selfSchema.UniqueKeys, uniqueKey)
|
||||
|
||||
@ -23,6 +23,7 @@ import (
|
||||
"github.com/pingcap/tidb/pkg/parser/mysql"
|
||||
"github.com/pingcap/tidb/pkg/planner/core/base"
|
||||
"github.com/pingcap/tidb/pkg/planner/core/operator/logicalop"
|
||||
ruleutil "github.com/pingcap/tidb/pkg/planner/core/rule/util"
|
||||
"github.com/pingcap/tidb/pkg/planner/property"
|
||||
"github.com/pingcap/tidb/pkg/types"
|
||||
"github.com/pingcap/tidb/pkg/util/plancodec"
|
||||
@ -98,7 +99,7 @@ func (is *LogicalIndexScan) BuildKeyInfo(selfSchema *expression.Schema, _ []*exp
|
||||
if path.IsTablePath() {
|
||||
continue
|
||||
}
|
||||
if uniqueKey, newKey := checkIndexCanBeKey(path.Index, is.Columns, selfSchema); newKey != nil {
|
||||
if uniqueKey, newKey := ruleutil.CheckIndexCanBeKey(path.Index, is.Columns, selfSchema); newKey != nil {
|
||||
selfSchema.Keys = append(selfSchema.Keys, newKey)
|
||||
} else if uniqueKey != nil {
|
||||
selfSchema.UniqueKeys = append(selfSchema.UniqueKeys, uniqueKey)
|
||||
|
||||
@ -288,7 +288,7 @@ func (p *LogicalJoin) PredicatePushDown(predicates []expression.Expression, opt
|
||||
utilfuncp.AddSelection(p, lCh, leftRet, 0, opt)
|
||||
utilfuncp.AddSelection(p, rCh, rightRet, 1, opt)
|
||||
p.updateEQCond()
|
||||
buildKeyInfo(p)
|
||||
ruleutil.BuildKeyInfoPortal(p)
|
||||
return ret, p.Self()
|
||||
}
|
||||
|
||||
|
||||
@ -152,7 +152,7 @@ func (p *LogicalSelection) BuildKeyInfo(selfSchema *expression.Schema, childSche
|
||||
}
|
||||
}
|
||||
}
|
||||
p.SetMaxOneRow(checkMaxOneRowCond(eqCols, childSchema[0]))
|
||||
p.SetMaxOneRow(ruleutil.CheckMaxOneRowCond(eqCols, childSchema[0]))
|
||||
}
|
||||
|
||||
// PushDownTopN inherits BaseLogicalPlan.<5th> implementation.
|
||||
|
||||
@ -39,6 +39,7 @@ import (
|
||||
"github.com/pingcap/tidb/pkg/parser/model"
|
||||
"github.com/pingcap/tidb/pkg/parser/mysql"
|
||||
"github.com/pingcap/tidb/pkg/planner/core/base"
|
||||
"github.com/pingcap/tidb/pkg/planner/core/rule"
|
||||
"github.com/pingcap/tidb/pkg/planner/property"
|
||||
"github.com/pingcap/tidb/pkg/planner/util/debugtrace"
|
||||
"github.com/pingcap/tidb/pkg/planner/util/optimizetrace"
|
||||
@ -96,32 +97,32 @@ const (
|
||||
flagResolveExpand
|
||||
)
|
||||
|
||||
var optRuleList = []logicalOptRule{
|
||||
&gcSubstituter{},
|
||||
&columnPruner{},
|
||||
&resultReorder{},
|
||||
&buildKeySolver{},
|
||||
&decorrelateSolver{},
|
||||
&semiJoinRewriter{},
|
||||
&aggregationEliminator{},
|
||||
&skewDistinctAggRewriter{},
|
||||
&projectionEliminator{},
|
||||
&maxMinEliminator{},
|
||||
&constantPropagationSolver{},
|
||||
&convertOuterToInnerJoin{},
|
||||
&ppdSolver{},
|
||||
&outerJoinEliminator{},
|
||||
&partitionProcessor{},
|
||||
&collectPredicateColumnsPoint{},
|
||||
&aggregationPushDownSolver{},
|
||||
&deriveTopNFromWindow{},
|
||||
&predicateSimplification{},
|
||||
&pushDownTopNOptimizer{},
|
||||
&syncWaitStatsLoadPoint{},
|
||||
&joinReOrderSolver{},
|
||||
&columnPruner{}, // column pruning again at last, note it will mess up the results of buildKeySolver
|
||||
&pushDownSequenceSolver{},
|
||||
&resolveExpand{},
|
||||
var optRuleList = []base.LogicalOptRule{
|
||||
&GcSubstituter{},
|
||||
&ColumnPruner{},
|
||||
&ResultReorder{},
|
||||
&rule.BuildKeySolver{},
|
||||
&DecorrelateSolver{},
|
||||
&SemiJoinRewriter{},
|
||||
&AggregationEliminator{},
|
||||
&SkewDistinctAggRewriter{},
|
||||
&ProjectionEliminator{},
|
||||
&MaxMinEliminator{},
|
||||
&ConstantPropagationSolver{},
|
||||
&ConvertOuterToInnerJoin{},
|
||||
&PPDSolver{},
|
||||
&OuterJoinEliminator{},
|
||||
&PartitionProcessor{},
|
||||
&CollectPredicateColumnsPoint{},
|
||||
&AggregationPushDownSolver{},
|
||||
&DeriveTopNFromWindow{},
|
||||
&PredicateSimplification{},
|
||||
&PushDownTopNOptimizer{},
|
||||
&SyncWaitStatsLoadPoint{},
|
||||
&JoinReOrderSolver{},
|
||||
&ColumnPruner{}, // column pruning again at last, note it will mess up the results of buildKeySolver
|
||||
&PushDownSequenceSolver{},
|
||||
&ResolveExpand{},
|
||||
}
|
||||
|
||||
// Interaction Rule List
|
||||
@ -129,20 +130,7 @@ var optRuleList = []logicalOptRule{
|
||||
1. The related rule has been trigger and changed the plan
|
||||
2. The interaction rule is enabled
|
||||
*/
|
||||
var optInteractionRuleList = map[logicalOptRule]logicalOptRule{}
|
||||
|
||||
// logicalOptRule means a logical optimizing rule, which contains decorrelate, ppd, column pruning, etc.
|
||||
type logicalOptRule interface {
|
||||
/* Return Parameters:
|
||||
1. base.LogicalPlan: The optimized base.LogicalPlan after rule is applied
|
||||
2. bool: Used to judge whether the plan is changed or not by logical rule.
|
||||
If the plan is changed, it will return true.
|
||||
The default value is false. It means that no interaction rule will be triggered.
|
||||
3. error: If there is error during the rule optimizer, it will be thrown
|
||||
*/
|
||||
optimize(context.Context, base.LogicalPlan, *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error)
|
||||
name() string
|
||||
}
|
||||
var optInteractionRuleList = map[base.LogicalOptRule]base.LogicalOptRule{}
|
||||
|
||||
// BuildLogicalPlanForTest builds a logical plan for testing purpose from ast.Node.
|
||||
func BuildLogicalPlanForTest(ctx context.Context, sctx sessionctx.Context, node ast.Node, infoSchema infoschema.InfoSchema) (base.Plan, error) {
|
||||
@ -999,7 +987,7 @@ func logicalOptimize(ctx context.Context, flag uint64, logic base.LogicalPlan) (
|
||||
}()
|
||||
}
|
||||
var err error
|
||||
var againRuleList []logicalOptRule
|
||||
var againRuleList []base.LogicalOptRule
|
||||
for i, rule := range optRuleList {
|
||||
// The order of flags is same as the order of optRule in the list.
|
||||
// We use a bitmask to record which opt rules should be used. If the i-th bit is 1, it means we should
|
||||
@ -1007,9 +995,9 @@ func logicalOptimize(ctx context.Context, flag uint64, logic base.LogicalPlan) (
|
||||
if flag&(1<<uint(i)) == 0 || isLogicalRuleDisabled(rule) {
|
||||
continue
|
||||
}
|
||||
opt.AppendBeforeRuleOptimize(i, rule.name(), logic.BuildPlanTrace)
|
||||
opt.AppendBeforeRuleOptimize(i, rule.Name(), logic.BuildPlanTrace)
|
||||
var planChanged bool
|
||||
logic, planChanged, err = rule.optimize(ctx, logic, opt)
|
||||
logic, planChanged, err = rule.Optimize(ctx, logic, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1022,8 +1010,8 @@ func logicalOptimize(ctx context.Context, flag uint64, logic base.LogicalPlan) (
|
||||
|
||||
// Trigger the interaction rule
|
||||
for i, rule := range againRuleList {
|
||||
opt.AppendBeforeRuleOptimize(i, rule.name(), logic.BuildPlanTrace)
|
||||
logic, _, err = rule.optimize(ctx, logic, opt)
|
||||
opt.AppendBeforeRuleOptimize(i, rule.Name(), logic.BuildPlanTrace)
|
||||
logic, _, err = rule.Optimize(ctx, logic, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1033,8 +1021,8 @@ func logicalOptimize(ctx context.Context, flag uint64, logic base.LogicalPlan) (
|
||||
return logic, err
|
||||
}
|
||||
|
||||
func isLogicalRuleDisabled(r logicalOptRule) bool {
|
||||
disabled := DefaultDisabledLogicalRulesList.Load().(set.StringSet).Exist(r.name())
|
||||
func isLogicalRuleDisabled(r base.LogicalOptRule) bool {
|
||||
disabled := DefaultDisabledLogicalRulesList.Load().(set.StringSet).Exist(r.Name())
|
||||
return disabled
|
||||
}
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@ import (
|
||||
// idx in the partition definitions array, use pi.Definitions[idx] to get the partition ID
|
||||
func PartitionPruning(ctx base.PlanContext, tbl table.PartitionedTable, conds []expression.Expression, partitionNames []model.CIStr,
|
||||
columns []*expression.Column, names types.NameSlice) ([]int, error) {
|
||||
s := partitionProcessor{}
|
||||
s := PartitionProcessor{}
|
||||
pi := tbl.Meta().Partition
|
||||
switch pi.Type {
|
||||
case model.PartitionTypeHash, model.PartitionTypeKey:
|
||||
|
||||
17
pkg/planner/core/rule/BUILD.bazel
Normal file
17
pkg/planner/core/rule/BUILD.bazel
Normal file
@ -0,0 +1,17 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "rule",
|
||||
srcs = [
|
||||
"rule_build_key_info.go",
|
||||
"rule_init.go",
|
||||
],
|
||||
importpath = "github.com/pingcap/tidb/pkg/planner/core/rule",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/expression",
|
||||
"//pkg/planner/core/base",
|
||||
"//pkg/planner/core/rule/util",
|
||||
"//pkg/planner/util/optimizetrace",
|
||||
],
|
||||
)
|
||||
54
pkg/planner/core/rule/rule_build_key_info.go
Normal file
54
pkg/planner/core/rule/rule_build_key_info.go
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2024 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,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rule
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pingcap/tidb/pkg/expression"
|
||||
"github.com/pingcap/tidb/pkg/planner/core/base"
|
||||
"github.com/pingcap/tidb/pkg/planner/util/optimizetrace"
|
||||
)
|
||||
|
||||
// BuildKeySolver is used to build key info for logical plan.
|
||||
type BuildKeySolver struct{}
|
||||
|
||||
// *************************** start implementation of LogicalOptRule interface ***************************
|
||||
|
||||
// Name implements base.LogicalOptRule.<0th> interface.
|
||||
func (*BuildKeySolver) Name() string {
|
||||
return "build_keys"
|
||||
}
|
||||
|
||||
// Optimize implements base.LogicalOptRule.<1st> interface.
|
||||
func (*BuildKeySolver) Optimize(_ context.Context, p base.LogicalPlan, _ *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
buildKeyInfo(p)
|
||||
return p, planChanged, nil
|
||||
}
|
||||
|
||||
// **************************** end implementation of LogicalOptRule interface ****************************
|
||||
|
||||
// buildKeyInfo recursively calls base.LogicalPlan's BuildKeyInfo method.
|
||||
func buildKeyInfo(lp base.LogicalPlan) {
|
||||
for _, child := range lp.Children() {
|
||||
buildKeyInfo(child)
|
||||
}
|
||||
childSchema := make([]*expression.Schema, len(lp.Children()))
|
||||
for i, child := range lp.Children() {
|
||||
childSchema[i] = child.Schema()
|
||||
}
|
||||
lp.BuildKeyInfo(lp.Schema(), childSchema)
|
||||
}
|
||||
26
pkg/planner/core/rule/rule_init.go
Normal file
26
pkg/planner/core/rule/rule_init.go
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2024 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,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rule
|
||||
|
||||
import "github.com/pingcap/tidb/pkg/planner/core/rule/util"
|
||||
|
||||
// rule/pkg should rely on operator/pkg to do type check and dig in and out,
|
||||
// rule/util doesn't have to rely on rule/pkg, but it can be put with rule
|
||||
// handling logic, and be referenced by operator/pkg.
|
||||
// the core usage only care and call about the rule/pkg and operator/pkg.
|
||||
|
||||
func init() {
|
||||
util.BuildKeyInfoPortal = buildKeyInfo
|
||||
}
|
||||
@ -2,8 +2,16 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "util",
|
||||
srcs = ["misc.go"],
|
||||
srcs = [
|
||||
"build_key_info_misc.go",
|
||||
"misc.go",
|
||||
],
|
||||
importpath = "github.com/pingcap/tidb/pkg/planner/core/rule/util",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//pkg/expression"],
|
||||
deps = [
|
||||
"//pkg/expression",
|
||||
"//pkg/parser/model",
|
||||
"//pkg/parser/mysql",
|
||||
"//pkg/planner/core/base",
|
||||
],
|
||||
)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 PingCAP, Inc.
|
||||
// Copyright 2024 PingCAP, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -12,41 +12,18 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package core
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pingcap/tidb/pkg/expression"
|
||||
"github.com/pingcap/tidb/pkg/parser/model"
|
||||
"github.com/pingcap/tidb/pkg/parser/mysql"
|
||||
"github.com/pingcap/tidb/pkg/planner/core/base"
|
||||
"github.com/pingcap/tidb/pkg/planner/util/optimizetrace"
|
||||
)
|
||||
|
||||
type buildKeySolver struct{}
|
||||
|
||||
func (*buildKeySolver) optimize(_ context.Context, p base.LogicalPlan, _ *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
buildKeyInfo(p)
|
||||
return p, planChanged, nil
|
||||
}
|
||||
|
||||
// buildKeyInfo recursively calls base.LogicalPlan's BuildKeyInfo method.
|
||||
func buildKeyInfo(lp base.LogicalPlan) {
|
||||
for _, child := range lp.Children() {
|
||||
buildKeyInfo(child)
|
||||
}
|
||||
childSchema := make([]*expression.Schema, len(lp.Children()))
|
||||
for i, child := range lp.Children() {
|
||||
childSchema[i] = child.Schema()
|
||||
}
|
||||
lp.BuildKeyInfo(lp.Schema(), childSchema)
|
||||
}
|
||||
|
||||
// If a condition is the form of (uniqueKey = constant) or (uniqueKey = Correlated column), it returns at most one row.
|
||||
// This function will check it.
|
||||
func checkMaxOneRowCond(eqColIDs map[int64]struct{}, childSchema *expression.Schema) bool {
|
||||
// CheckMaxOneRowCond check if a condition is the form of (uniqueKey = constant) or (uniqueKey =
|
||||
// Correlated column), it returns at most one row.
|
||||
func CheckMaxOneRowCond(eqColIDs map[int64]struct{}, childSchema *expression.Schema) bool {
|
||||
if len(eqColIDs) == 0 {
|
||||
return false
|
||||
}
|
||||
@ -70,8 +47,8 @@ func checkMaxOneRowCond(eqColIDs map[int64]struct{}, childSchema *expression.Sch
|
||||
return false
|
||||
}
|
||||
|
||||
// checkIndexCanBeKey checks whether an Index can be a Key in schema.
|
||||
func checkIndexCanBeKey(idx *model.IndexInfo, columns []*model.ColumnInfo, schema *expression.Schema) (uniqueKey, newKey expression.KeyInfo) {
|
||||
// CheckIndexCanBeKey checks whether an Index can be a Key in schema.
|
||||
func CheckIndexCanBeKey(idx *model.IndexInfo, columns []*model.ColumnInfo, schema *expression.Schema) (uniqueKey, newKey expression.KeyInfo) {
|
||||
if !idx.Unique {
|
||||
return nil, nil
|
||||
}
|
||||
@ -110,6 +87,5 @@ func checkIndexCanBeKey(idx *model.IndexInfo, columns []*model.ColumnInfo, schem
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (*buildKeySolver) name() string {
|
||||
return "build_keys"
|
||||
}
|
||||
// BuildKeyInfoPortal is a hook for other packages to build key info for logical plan.
|
||||
var BuildKeyInfoPortal func(lp base.LogicalPlan)
|
||||
@ -29,7 +29,8 @@ import (
|
||||
"github.com/pingcap/tidb/pkg/types"
|
||||
)
|
||||
|
||||
type aggregationEliminator struct {
|
||||
// AggregationEliminator is used to eliminate aggregation grouped by unique key.
|
||||
type AggregationEliminator struct {
|
||||
aggregationEliminateChecker
|
||||
}
|
||||
|
||||
@ -256,11 +257,12 @@ func wrapCastFunction(ctx expression.BuildContext, arg expression.Expression, ta
|
||||
return expression.BuildCastFunction(ctx, arg, targetTp)
|
||||
}
|
||||
|
||||
func (a *aggregationEliminator) optimize(ctx context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements the base.LogicalOptRule.<0th> interface.
|
||||
func (a *AggregationEliminator) Optimize(ctx context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
newChildren := make([]base.LogicalPlan, 0, len(p.Children()))
|
||||
for _, child := range p.Children() {
|
||||
newChild, planChanged, err := a.optimize(ctx, child, opt)
|
||||
newChild, planChanged, err := a.Optimize(ctx, child, opt)
|
||||
if err != nil {
|
||||
return nil, planChanged, err
|
||||
}
|
||||
@ -278,6 +280,7 @@ func (a *aggregationEliminator) optimize(ctx context.Context, p base.LogicalPlan
|
||||
return p, planChanged, nil
|
||||
}
|
||||
|
||||
func (*aggregationEliminator) name() string {
|
||||
// Name implements the base.LogicalOptRule.<1st> interface.
|
||||
func (*AggregationEliminator) Name() string {
|
||||
return "aggregation_eliminate"
|
||||
}
|
||||
|
||||
@ -26,12 +26,14 @@ import (
|
||||
"github.com/pingcap/tidb/pkg/parser/mysql"
|
||||
"github.com/pingcap/tidb/pkg/planner/core/base"
|
||||
"github.com/pingcap/tidb/pkg/planner/core/operator/logicalop"
|
||||
ruleutil "github.com/pingcap/tidb/pkg/planner/core/rule/util"
|
||||
"github.com/pingcap/tidb/pkg/planner/util"
|
||||
"github.com/pingcap/tidb/pkg/planner/util/optimizetrace"
|
||||
"github.com/pingcap/tidb/pkg/types"
|
||||
)
|
||||
|
||||
type aggregationPushDownSolver struct {
|
||||
// AggregationPushDownSolver is a rule that pushes down aggregation functions to the child of LogicalJoin.
|
||||
type AggregationPushDownSolver struct {
|
||||
aggregationEliminateChecker
|
||||
}
|
||||
|
||||
@ -42,7 +44,7 @@ type aggregationPushDownSolver struct {
|
||||
// It's easy to see that max, min, first row is decomposable, no matter whether it's distinct, but sum(distinct) and
|
||||
// count(distinct) is not.
|
||||
// Currently we don't support avg and concat.
|
||||
func (*aggregationPushDownSolver) isDecomposableWithJoin(fun *aggregation.AggFuncDesc) bool {
|
||||
func (*AggregationPushDownSolver) isDecomposableWithJoin(fun *aggregation.AggFuncDesc) bool {
|
||||
if len(fun.OrderByItems) > 0 {
|
||||
return false
|
||||
}
|
||||
@ -59,7 +61,7 @@ func (*aggregationPushDownSolver) isDecomposableWithJoin(fun *aggregation.AggFun
|
||||
}
|
||||
}
|
||||
|
||||
func (*aggregationPushDownSolver) isDecomposableWithUnion(fun *aggregation.AggFuncDesc) bool {
|
||||
func (*AggregationPushDownSolver) isDecomposableWithUnion(fun *aggregation.AggFuncDesc) bool {
|
||||
if len(fun.OrderByItems) > 0 {
|
||||
return false
|
||||
}
|
||||
@ -77,7 +79,7 @@ func (*aggregationPushDownSolver) isDecomposableWithUnion(fun *aggregation.AggFu
|
||||
|
||||
// getAggFuncChildIdx gets which children it belongs to.
|
||||
// 0 stands for left, 1 stands for right, -1 stands for both, 2 stands for neither (e.g. count(*), sum(1) ...)
|
||||
func (*aggregationPushDownSolver) getAggFuncChildIdx(aggFunc *aggregation.AggFuncDesc, lSchema, rSchema *expression.Schema) int {
|
||||
func (*AggregationPushDownSolver) getAggFuncChildIdx(aggFunc *aggregation.AggFuncDesc, lSchema, rSchema *expression.Schema) int {
|
||||
fromLeft, fromRight := false, false
|
||||
var cols []*expression.Column
|
||||
cols = expression.ExtractColumnsFromExpressions(cols, aggFunc.Args, nil)
|
||||
@ -102,7 +104,7 @@ func (*aggregationPushDownSolver) getAggFuncChildIdx(aggFunc *aggregation.AggFun
|
||||
// collectAggFuncs collects all aggregate functions and splits them into two parts: "leftAggFuncs" and "rightAggFuncs" whose
|
||||
// arguments are all from left child or right child separately. If some aggregate functions have the arguments that have
|
||||
// columns both from left and right children, the whole aggregation is forbidden to push down.
|
||||
func (a *aggregationPushDownSolver) collectAggFuncs(agg *LogicalAggregation, join *LogicalJoin) (valid bool, leftAggFuncs, rightAggFuncs []*aggregation.AggFuncDesc) {
|
||||
func (a *AggregationPushDownSolver) collectAggFuncs(agg *LogicalAggregation, join *LogicalJoin) (valid bool, leftAggFuncs, rightAggFuncs []*aggregation.AggFuncDesc) {
|
||||
valid = true
|
||||
leftChild := join.Children()[0]
|
||||
rightChild := join.Children()[1]
|
||||
@ -145,7 +147,7 @@ func (a *aggregationPushDownSolver) collectAggFuncs(agg *LogicalAggregation, joi
|
||||
// query should be "SELECT SUM(B.agg) FROM A, (SELECT SUM(id) as agg, c1, c2, c3 FROM B GROUP BY id, c1, c2, c3) as B
|
||||
// WHERE A.c1 = B.c1 AND A.c2 != B.c2 GROUP BY B.c3". As you see, all the columns appearing in join-conditions should be
|
||||
// treated as group by columns in join subquery.
|
||||
func (a *aggregationPushDownSolver) collectGbyCols(agg *LogicalAggregation, join *LogicalJoin) (leftGbyCols, rightGbyCols []*expression.Column) {
|
||||
func (a *AggregationPushDownSolver) collectGbyCols(agg *LogicalAggregation, join *LogicalJoin) (leftGbyCols, rightGbyCols []*expression.Column) {
|
||||
leftChild := join.Children()[0]
|
||||
ctx := agg.SCtx()
|
||||
for _, gbyExpr := range agg.GroupByItems {
|
||||
@ -184,7 +186,7 @@ func (a *aggregationPushDownSolver) collectGbyCols(agg *LogicalAggregation, join
|
||||
return
|
||||
}
|
||||
|
||||
func (a *aggregationPushDownSolver) splitAggFuncsAndGbyCols(agg *LogicalAggregation, join *LogicalJoin) (valid bool,
|
||||
func (a *AggregationPushDownSolver) splitAggFuncsAndGbyCols(agg *LogicalAggregation, join *LogicalJoin) (valid bool,
|
||||
leftAggFuncs, rightAggFuncs []*aggregation.AggFuncDesc,
|
||||
leftGbyCols, rightGbyCols []*expression.Column) {
|
||||
valid, leftAggFuncs, rightAggFuncs = a.collectAggFuncs(agg, join)
|
||||
@ -196,7 +198,7 @@ func (a *aggregationPushDownSolver) splitAggFuncsAndGbyCols(agg *LogicalAggregat
|
||||
}
|
||||
|
||||
// addGbyCol adds a column to gbyCols. If a group by column has existed, it will not be added repeatedly.
|
||||
func (*aggregationPushDownSolver) addGbyCol(ctx base.PlanContext, gbyCols []*expression.Column, cols ...*expression.Column) []*expression.Column {
|
||||
func (*AggregationPushDownSolver) addGbyCol(ctx base.PlanContext, gbyCols []*expression.Column, cols ...*expression.Column) []*expression.Column {
|
||||
for _, c := range cols {
|
||||
duplicate := false
|
||||
for _, gbyCol := range gbyCols {
|
||||
@ -213,13 +215,13 @@ func (*aggregationPushDownSolver) addGbyCol(ctx base.PlanContext, gbyCols []*exp
|
||||
}
|
||||
|
||||
// checkValidJoin checks if this join should be pushed across.
|
||||
func (*aggregationPushDownSolver) checkValidJoin(join *LogicalJoin) bool {
|
||||
func (*AggregationPushDownSolver) checkValidJoin(join *LogicalJoin) bool {
|
||||
return join.JoinType == InnerJoin || join.JoinType == LeftOuterJoin || join.JoinType == RightOuterJoin
|
||||
}
|
||||
|
||||
// decompose splits an aggregate function to two parts: a final mode function and a partial mode function. Currently
|
||||
// there are no differences between partial mode and complete mode, so we can confuse them.
|
||||
func (*aggregationPushDownSolver) decompose(ctx base.PlanContext, aggFunc *aggregation.AggFuncDesc,
|
||||
func (*AggregationPushDownSolver) decompose(ctx base.PlanContext, aggFunc *aggregation.AggFuncDesc,
|
||||
schema *expression.Schema, nullGenerating bool) ([]*aggregation.AggFuncDesc, *expression.Schema) {
|
||||
// Result is a slice because avg should be decomposed to sum and count. Currently we don't process this case.
|
||||
result := []*aggregation.AggFuncDesc{aggFunc.Clone()}
|
||||
@ -251,7 +253,7 @@ func (*aggregationPushDownSolver) decompose(ctx base.PlanContext, aggFunc *aggre
|
||||
// tryToPushDownAgg tries to push down an aggregate function into a join path. If all aggFuncs are first row, we won't
|
||||
// process it temporarily. If not, We will add additional group by columns and first row functions. We make a new aggregation operator.
|
||||
// If the pushed aggregation is grouped by unique key, it's no need to push it down.
|
||||
func (a *aggregationPushDownSolver) tryToPushDownAgg(oldAgg *LogicalAggregation, aggFuncs []*aggregation.AggFuncDesc, gbyCols []*expression.Column,
|
||||
func (a *AggregationPushDownSolver) tryToPushDownAgg(oldAgg *LogicalAggregation, aggFuncs []*aggregation.AggFuncDesc, gbyCols []*expression.Column,
|
||||
join *LogicalJoin, childIdx int, blockOffset int, opt *optimizetrace.LogicalOptimizeOp) (_ base.LogicalPlan, err error) {
|
||||
child := join.Children()[childIdx]
|
||||
if aggregation.IsAllFirstRow(aggFuncs) {
|
||||
@ -292,7 +294,7 @@ func (a *aggregationPushDownSolver) tryToPushDownAgg(oldAgg *LogicalAggregation,
|
||||
return agg, nil
|
||||
}
|
||||
|
||||
func (*aggregationPushDownSolver) getDefaultValues(agg *LogicalAggregation) ([]types.Datum, bool) {
|
||||
func (*AggregationPushDownSolver) getDefaultValues(agg *LogicalAggregation) ([]types.Datum, bool) {
|
||||
defaultValues := make([]types.Datum, 0, agg.Schema().Len())
|
||||
for _, aggFunc := range agg.AggFuncs {
|
||||
value, existsDefaultValue := aggFunc.EvalNullValueInOuterJoin(agg.SCtx().GetExprCtx(), agg.Children()[0].Schema())
|
||||
@ -304,7 +306,7 @@ func (*aggregationPushDownSolver) getDefaultValues(agg *LogicalAggregation) ([]t
|
||||
return defaultValues, true
|
||||
}
|
||||
|
||||
func (*aggregationPushDownSolver) checkAnyCountAndSum(aggFuncs []*aggregation.AggFuncDesc) bool {
|
||||
func (*AggregationPushDownSolver) checkAnyCountAndSum(aggFuncs []*aggregation.AggFuncDesc) bool {
|
||||
for _, fun := range aggFuncs {
|
||||
if fun.Name == ast.AggFuncSum || fun.Name == ast.AggFuncCount {
|
||||
return true
|
||||
@ -315,7 +317,7 @@ func (*aggregationPushDownSolver) checkAnyCountAndSum(aggFuncs []*aggregation.Ag
|
||||
|
||||
// checkAllArgsColumn checks whether the args in function are dedicated columns
|
||||
// eg: count(*) or sum(a+1) will return false while count(a) or sum(a) will return true
|
||||
func (*aggregationPushDownSolver) checkAllArgsColumn(fun *aggregation.AggFuncDesc) bool {
|
||||
func (*AggregationPushDownSolver) checkAllArgsColumn(fun *aggregation.AggFuncDesc) bool {
|
||||
for _, arg := range fun.Args {
|
||||
_, ok := arg.(*expression.Column)
|
||||
if !ok {
|
||||
@ -328,7 +330,7 @@ func (*aggregationPushDownSolver) checkAllArgsColumn(fun *aggregation.AggFuncDes
|
||||
// TODO:
|
||||
// 1. https://github.com/pingcap/tidb/issues/16355, push avg & distinct functions across join
|
||||
// 2. remove this method and use splitPartialAgg instead for clean code.
|
||||
func (a *aggregationPushDownSolver) makeNewAgg(ctx base.PlanContext, aggFuncs []*aggregation.AggFuncDesc,
|
||||
func (a *AggregationPushDownSolver) makeNewAgg(ctx base.PlanContext, aggFuncs []*aggregation.AggFuncDesc,
|
||||
gbyCols []*expression.Column, preferAggType uint, preferAggToCop bool, blockOffset int, nullGenerating bool) (*LogicalAggregation, error) {
|
||||
agg := LogicalAggregation{
|
||||
GroupByItems: expression.Column2Exprs(gbyCols),
|
||||
@ -360,7 +362,7 @@ func (a *aggregationPushDownSolver) makeNewAgg(ctx base.PlanContext, aggFuncs []
|
||||
return agg, nil
|
||||
}
|
||||
|
||||
func (*aggregationPushDownSolver) splitPartialAgg(agg *LogicalAggregation) (pushedAgg *LogicalAggregation) {
|
||||
func (*AggregationPushDownSolver) splitPartialAgg(agg *LogicalAggregation) (pushedAgg *LogicalAggregation) {
|
||||
partial, final, _ := BuildFinalModeAggregation(agg.SCtx(), &AggInfo{
|
||||
AggFuncs: agg.AggFuncs,
|
||||
GroupByItems: agg.GroupByItems,
|
||||
@ -386,7 +388,7 @@ func (*aggregationPushDownSolver) splitPartialAgg(agg *LogicalAggregation) (push
|
||||
|
||||
// pushAggCrossUnion will try to push the agg down to the union. If the new aggregation's group-by columns doesn't contain unique key.
|
||||
// We will return the new aggregation. Otherwise we will transform the aggregation to projection.
|
||||
func (*aggregationPushDownSolver) pushAggCrossUnion(agg *LogicalAggregation, unionSchema *expression.Schema, unionChild base.LogicalPlan) (base.LogicalPlan, error) {
|
||||
func (*AggregationPushDownSolver) pushAggCrossUnion(agg *LogicalAggregation, unionSchema *expression.Schema, unionChild base.LogicalPlan) (base.LogicalPlan, error) {
|
||||
ctx := agg.SCtx()
|
||||
newAgg := LogicalAggregation{
|
||||
AggFuncs: make([]*aggregation.AggFuncDesc, 0, len(agg.AggFuncs)),
|
||||
@ -437,13 +439,14 @@ func (*aggregationPushDownSolver) pushAggCrossUnion(agg *LogicalAggregation, uni
|
||||
return newAgg, nil
|
||||
}
|
||||
|
||||
func (a *aggregationPushDownSolver) optimize(_ context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements the base.LogicalOptRule.<0th> interface.
|
||||
func (a *AggregationPushDownSolver) Optimize(_ context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
newLogicalPlan, err := a.aggPushDown(p, opt)
|
||||
return newLogicalPlan, planChanged, err
|
||||
}
|
||||
|
||||
func (a *aggregationPushDownSolver) tryAggPushDownForUnion(union *LogicalUnionAll, agg *LogicalAggregation, opt *optimizetrace.LogicalOptimizeOp) error {
|
||||
func (a *AggregationPushDownSolver) tryAggPushDownForUnion(union *LogicalUnionAll, agg *LogicalAggregation, opt *optimizetrace.LogicalOptimizeOp) error {
|
||||
for _, aggFunc := range agg.AggFuncs {
|
||||
if !a.isDecomposableWithUnion(aggFunc) {
|
||||
return nil
|
||||
@ -478,7 +481,7 @@ func (a *aggregationPushDownSolver) tryAggPushDownForUnion(union *LogicalUnionAl
|
||||
}
|
||||
|
||||
// aggPushDown tries to push down aggregate functions to join paths.
|
||||
func (a *aggregationPushDownSolver) aggPushDown(p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (_ base.LogicalPlan, err error) {
|
||||
func (a *AggregationPushDownSolver) aggPushDown(p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (_ base.LogicalPlan, err error) {
|
||||
if agg, ok := p.(*LogicalAggregation); ok {
|
||||
proj := a.tryToEliminateAggregation(agg, opt)
|
||||
if proj != nil {
|
||||
@ -518,7 +521,7 @@ func (a *aggregationPushDownSolver) aggPushDown(p base.LogicalPlan, opt *optimiz
|
||||
} else if join.JoinType == RightOuterJoin {
|
||||
util.ResetNotNullFlag(join.Schema(), 0, lChild.Schema().Len())
|
||||
}
|
||||
buildKeyInfo(join)
|
||||
ruleutil.BuildKeyInfoPortal(join)
|
||||
// count(a) -> ifnull(col#x, 0, 1) in rewriteExpr of agg function, since col#x is already the final
|
||||
// pushed-down aggregation's result, we don't need to take every row as count 1 when they don't have
|
||||
// not-null flag in a.tryToEliminateAggregation(oldAgg, opt), which is not suitable here.
|
||||
@ -550,7 +553,7 @@ func (a *aggregationPushDownSolver) aggPushDown(p base.LogicalPlan, opt *optimiz
|
||||
}
|
||||
if changed {
|
||||
join.SetChildren(lChild, rChild)
|
||||
buildKeyInfo(join)
|
||||
ruleutil.BuildKeyInfoPortal(join)
|
||||
}
|
||||
}
|
||||
} else if proj, ok1 := child.(*logicalop.LogicalProjection); ok1 {
|
||||
@ -683,7 +686,8 @@ func (a *aggregationPushDownSolver) aggPushDown(p base.LogicalPlan, opt *optimiz
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (*aggregationPushDownSolver) name() string {
|
||||
// Name implements the base.LogicalOptRule.<1st> interface.
|
||||
func (*AggregationPushDownSolver) Name() string {
|
||||
return "aggregation_push_down"
|
||||
}
|
||||
|
||||
|
||||
@ -27,7 +27,8 @@ import (
|
||||
"github.com/pingcap/tidb/pkg/util/intset"
|
||||
)
|
||||
|
||||
type skewDistinctAggRewriter struct {
|
||||
// SkewDistinctAggRewriter rewrites group distinct aggregate into 2 level aggregates.
|
||||
type SkewDistinctAggRewriter struct {
|
||||
}
|
||||
|
||||
// skewDistinctAggRewriter will rewrite group distinct aggregate into 2 level aggregates, e.g.:
|
||||
@ -49,7 +50,7 @@ type skewDistinctAggRewriter struct {
|
||||
// - The aggregate has 1 and only 1 distinct aggregate function (limited to count, avg, sum)
|
||||
//
|
||||
// This rule is disabled by default. Use tidb_opt_skew_distinct_agg to enable the rule.
|
||||
func (a *skewDistinctAggRewriter) rewriteSkewDistinctAgg(agg *LogicalAggregation, opt *optimizetrace.LogicalOptimizeOp) base.LogicalPlan {
|
||||
func (a *SkewDistinctAggRewriter) rewriteSkewDistinctAgg(agg *LogicalAggregation, opt *optimizetrace.LogicalOptimizeOp) base.LogicalPlan {
|
||||
// only group aggregate is applicable
|
||||
if len(agg.GroupByItems) == 0 {
|
||||
return nil
|
||||
@ -237,7 +238,7 @@ func (a *skewDistinctAggRewriter) rewriteSkewDistinctAgg(agg *LogicalAggregation
|
||||
return proj
|
||||
}
|
||||
|
||||
func (*skewDistinctAggRewriter) isQualifiedAgg(aggFunc *aggregation.AggFuncDesc) bool {
|
||||
func (*SkewDistinctAggRewriter) isQualifiedAgg(aggFunc *aggregation.AggFuncDesc) bool {
|
||||
if aggFunc.Mode != aggregation.CompleteMode {
|
||||
return false
|
||||
}
|
||||
@ -276,11 +277,12 @@ func appendSkewDistinctAggRewriteTraceStep(agg *LogicalAggregation, result base.
|
||||
opt.AppendStepToCurrent(agg.ID(), agg.TP(), reason, action)
|
||||
}
|
||||
|
||||
func (a *skewDistinctAggRewriter) optimize(ctx context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements base.LogicalOptRule.<0th> interface.
|
||||
func (a *SkewDistinctAggRewriter) Optimize(ctx context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
newChildren := make([]base.LogicalPlan, 0, len(p.Children()))
|
||||
for _, child := range p.Children() {
|
||||
newChild, planChanged, err := a.optimize(ctx, child, opt)
|
||||
newChild, planChanged, err := a.Optimize(ctx, child, opt)
|
||||
if err != nil {
|
||||
return nil, planChanged, err
|
||||
}
|
||||
@ -297,6 +299,7 @@ func (a *skewDistinctAggRewriter) optimize(ctx context.Context, p base.LogicalPl
|
||||
return p, planChanged, nil
|
||||
}
|
||||
|
||||
func (*skewDistinctAggRewriter) name() string {
|
||||
// Name implements base.LogicalOptRule.<1st> interface.
|
||||
func (*SkewDistinctAggRewriter) Name() string {
|
||||
return "skew_distinct_agg_rewrite"
|
||||
}
|
||||
|
||||
@ -31,9 +31,11 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type collectPredicateColumnsPoint struct{}
|
||||
// CollectPredicateColumnsPoint collects the columns that are used in the predicates.
|
||||
type CollectPredicateColumnsPoint struct{}
|
||||
|
||||
func (collectPredicateColumnsPoint) optimize(_ context.Context, plan base.LogicalPlan, _ *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements LogicalOptRule.<0th> interface.
|
||||
func (CollectPredicateColumnsPoint) Optimize(_ context.Context, plan base.LogicalPlan, _ *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
if plan.SCtx().GetSessionVars().InRestrictedSQL {
|
||||
return plan, planChanged, nil
|
||||
@ -72,13 +74,16 @@ func (collectPredicateColumnsPoint) optimize(_ context.Context, plan base.Logica
|
||||
return plan, planChanged, nil
|
||||
}
|
||||
|
||||
func (collectPredicateColumnsPoint) name() string {
|
||||
// Name implements the base.LogicalOptRule.<1st> interface.
|
||||
func (CollectPredicateColumnsPoint) Name() string {
|
||||
return "collect_predicate_columns_point"
|
||||
}
|
||||
|
||||
type syncWaitStatsLoadPoint struct{}
|
||||
// SyncWaitStatsLoadPoint sync-wait for stats load point.
|
||||
type SyncWaitStatsLoadPoint struct{}
|
||||
|
||||
func (syncWaitStatsLoadPoint) optimize(_ context.Context, plan base.LogicalPlan, _ *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements the base.LogicalOptRule.<0th> interface.
|
||||
func (SyncWaitStatsLoadPoint) Optimize(_ context.Context, plan base.LogicalPlan, _ *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
if plan.SCtx().GetSessionVars().InRestrictedSQL {
|
||||
return plan, planChanged, nil
|
||||
@ -90,7 +95,8 @@ func (syncWaitStatsLoadPoint) optimize(_ context.Context, plan base.LogicalPlan,
|
||||
return plan, planChanged, err
|
||||
}
|
||||
|
||||
func (syncWaitStatsLoadPoint) name() string {
|
||||
// Name implements the base.LogicalOptRule.<1st> interface.
|
||||
func (SyncWaitStatsLoadPoint) Name() string {
|
||||
return "sync_wait_stats_load_point"
|
||||
}
|
||||
|
||||
|
||||
@ -30,10 +30,12 @@ import (
|
||||
"github.com/pingcap/tidb/pkg/util/intest"
|
||||
)
|
||||
|
||||
type columnPruner struct {
|
||||
// ColumnPruner is used to prune unnecessary columns.
|
||||
type ColumnPruner struct {
|
||||
}
|
||||
|
||||
func (*columnPruner) optimize(_ context.Context, lp base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements base.LogicalOptRule.<0th> interface.
|
||||
func (*ColumnPruner) Optimize(_ context.Context, lp base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
lp, err := lp.PruneColumns(slices.Clone(lp.Schema().Columns), opt)
|
||||
if err != nil {
|
||||
@ -93,7 +95,8 @@ func pruneByItems(p base.LogicalPlan, old []*util.ByItems, opt *optimizetrace.Lo
|
||||
return
|
||||
}
|
||||
|
||||
func (*columnPruner) name() string {
|
||||
// Name implements base.LogicalOptRule.<1st> interface.
|
||||
func (*ColumnPruner) Name() string {
|
||||
return "column_prune"
|
||||
}
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ import (
|
||||
"github.com/pingcap/tidb/pkg/planner/util/optimizetrace"
|
||||
)
|
||||
|
||||
// constantPropagationSolver can support constant propagated cross-query block.
|
||||
// ConstantPropagationSolver can support constant propagated cross-query block.
|
||||
// This is a logical optimize rule.
|
||||
// It mainly used for the sub query in FromList and propagated the constant predicate
|
||||
// from sub query to outer query.
|
||||
@ -41,9 +41,10 @@ import (
|
||||
// Steps 1 and 2 will be called recursively
|
||||
// 3. (ppdSolver in rule_predicate_push_down.go) Push down constant predicate
|
||||
// and propagate constant predicate into other side. 't.id>1'
|
||||
type constantPropagationSolver struct {
|
||||
type ConstantPropagationSolver struct {
|
||||
}
|
||||
|
||||
// Optimize implements base.LogicalOptRule.<0th> interface.
|
||||
// **Preorder traversal** of logic tree
|
||||
// Step1: constant propagation current plan node
|
||||
// Step2: optimize all of child
|
||||
@ -52,7 +53,7 @@ type constantPropagationSolver struct {
|
||||
// which is mainly implemented in the interface "constantPropagation" of LogicalPlan.
|
||||
// Currently only the Logical Join implements this function. (Used for the subquery in FROM List)
|
||||
// In the future, the Logical Apply will implements this function. (Used for the subquery in WHERE or SELECT list)
|
||||
func (cp *constantPropagationSolver) optimize(_ context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
func (cp *ConstantPropagationSolver) Optimize(_ context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
// constant propagation root plan
|
||||
newRoot := p.ConstantPropagation(nil, 0, opt)
|
||||
@ -69,7 +70,7 @@ func (cp *constantPropagationSolver) optimize(_ context.Context, p base.LogicalP
|
||||
}
|
||||
|
||||
// execOptimize optimize constant propagation exclude root plan node
|
||||
func (cp *constantPropagationSolver) execOptimize(currentPlan base.LogicalPlan, parentPlan base.LogicalPlan, currentChildIdx int, opt *optimizetrace.LogicalOptimizeOp) {
|
||||
func (cp *ConstantPropagationSolver) execOptimize(currentPlan base.LogicalPlan, parentPlan base.LogicalPlan, currentChildIdx int, opt *optimizetrace.LogicalOptimizeOp) {
|
||||
if parentPlan == nil {
|
||||
// Attention: The function 'execOptimize' could not handle the root plan, so the parent plan could not be nil.
|
||||
return
|
||||
@ -82,7 +83,8 @@ func (cp *constantPropagationSolver) execOptimize(currentPlan base.LogicalPlan,
|
||||
}
|
||||
}
|
||||
|
||||
func (*constantPropagationSolver) name() string {
|
||||
// Name implements base.LogicalOptRule.<1st> interface.
|
||||
func (*ConstantPropagationSolver) Name() string {
|
||||
return "constant_propagation"
|
||||
}
|
||||
|
||||
|
||||
@ -105,10 +105,10 @@ func extractOuterApplyCorrelatedColsHelper(p base.PhysicalPlan, outerSchemas []*
|
||||
return newCorCols
|
||||
}
|
||||
|
||||
// decorrelateSolver tries to convert apply plan to join plan.
|
||||
type decorrelateSolver struct{}
|
||||
// DecorrelateSolver tries to convert apply plan to join plan.
|
||||
type DecorrelateSolver struct{}
|
||||
|
||||
func (*decorrelateSolver) aggDefaultValueMap(agg *LogicalAggregation) map[int]*expression.Constant {
|
||||
func (*DecorrelateSolver) aggDefaultValueMap(agg *LogicalAggregation) map[int]*expression.Constant {
|
||||
defaultValueMap := make(map[int]*expression.Constant, len(agg.AggFuncs))
|
||||
for i, f := range agg.AggFuncs {
|
||||
switch f.Name {
|
||||
@ -121,8 +121,8 @@ func (*decorrelateSolver) aggDefaultValueMap(agg *LogicalAggregation) map[int]*e
|
||||
return defaultValueMap
|
||||
}
|
||||
|
||||
// optimize implements logicalOptRule interface.
|
||||
func (s *decorrelateSolver) optimize(ctx context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements base.LogicalOptRule.<0th> interface.
|
||||
func (s *DecorrelateSolver) Optimize(ctx context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
if apply, ok := p.(*LogicalApply); ok {
|
||||
outerPlan := apply.Children()[0]
|
||||
@ -148,13 +148,13 @@ func (s *decorrelateSolver) optimize(ctx context.Context, p base.LogicalPlan, op
|
||||
innerPlan = sel.Children()[0]
|
||||
apply.SetChildren(outerPlan, innerPlan)
|
||||
appendRemoveSelectionTraceStep(apply, sel, opt)
|
||||
return s.optimize(ctx, p, opt)
|
||||
return s.Optimize(ctx, p, opt)
|
||||
} else if m, ok := innerPlan.(*logicalop.LogicalMaxOneRow); ok {
|
||||
if m.Children()[0].MaxOneRow() {
|
||||
innerPlan = m.Children()[0]
|
||||
apply.SetChildren(outerPlan, innerPlan)
|
||||
appendRemoveMaxOneRowTraceStep(m, opt)
|
||||
return s.optimize(ctx, p, opt)
|
||||
return s.Optimize(ctx, p, opt)
|
||||
}
|
||||
} else if proj, ok := innerPlan.(*logicalop.LogicalProjection); ok {
|
||||
// After the column pruning, some expressions in the projection operator may be pruned.
|
||||
@ -202,7 +202,7 @@ func (s *decorrelateSolver) optimize(ctx context.Context, p base.LogicalPlan, op
|
||||
proj.SetSchema(apply.Schema())
|
||||
proj.Exprs = append(expression.Column2Exprs(outerPlan.Schema().Clone().Columns), proj.Exprs...)
|
||||
apply.SetSchema(expression.MergeSchema(outerPlan.Schema(), innerPlan.Schema()))
|
||||
np, planChanged, err := s.optimize(ctx, p, opt)
|
||||
np, planChanged, err := s.Optimize(ctx, p, opt)
|
||||
if err != nil {
|
||||
return nil, planChanged, err
|
||||
}
|
||||
@ -211,7 +211,7 @@ func (s *decorrelateSolver) optimize(ctx context.Context, p base.LogicalPlan, op
|
||||
return proj, planChanged, nil
|
||||
}
|
||||
appendRemoveProjTraceStep(apply, proj, opt)
|
||||
return s.optimize(ctx, p, opt)
|
||||
return s.Optimize(ctx, p, opt)
|
||||
} else if li, ok := innerPlan.(*logicalop.LogicalLimit); ok {
|
||||
// The presence of 'limit' in 'exists' will make the plan not optimal, so we need to decorrelate the 'limit' of subquery in optimization.
|
||||
// e.g. select count(*) from test t1 where exists (select value from test t2 where t1.id = t2.id limit 1); When using 'limit' in subquery, the plan will not optimal.
|
||||
@ -228,7 +228,7 @@ func (s *decorrelateSolver) optimize(ctx context.Context, p base.LogicalPlan, op
|
||||
innerPlan = li.Children()[0]
|
||||
apply.SetChildren(outerPlan, innerPlan)
|
||||
appendRemoveLimitTraceStep(li, opt)
|
||||
return s.optimize(ctx, p, opt)
|
||||
return s.Optimize(ctx, p, opt)
|
||||
}
|
||||
} else if agg, ok := innerPlan.(*LogicalAggregation); ok {
|
||||
if apply.CanPullUpAgg() && agg.canPullUp() {
|
||||
@ -278,7 +278,7 @@ func (s *decorrelateSolver) optimize(ctx context.Context, p base.LogicalPlan, op
|
||||
newAggFuncs = append(newAggFuncs, desc)
|
||||
}
|
||||
agg.AggFuncs = newAggFuncs
|
||||
np, planChanged, err := s.optimize(ctx, p, opt)
|
||||
np, planChanged, err := s.Optimize(ctx, p, opt)
|
||||
if err != nil {
|
||||
return nil, planChanged, err
|
||||
}
|
||||
@ -356,7 +356,7 @@ func (s *decorrelateSolver) optimize(ctx context.Context, p base.LogicalPlan, op
|
||||
appendAddProjTraceStep(apply, proj, opt)
|
||||
}
|
||||
appendModifyAggTraceStep(outerPlan, apply, agg, sel, appendedGroupByCols, appendedAggFuncs, eqCondWithCorCol, opt)
|
||||
return s.optimize(ctx, p, opt)
|
||||
return s.Optimize(ctx, p, opt)
|
||||
}
|
||||
sel.Conditions = originalExpr
|
||||
apply.CorCols = coreusage.ExtractCorColumnsBySchema4LogicalPlan(apply.Children()[1], apply.Children()[0].Schema())
|
||||
@ -368,7 +368,7 @@ func (s *decorrelateSolver) optimize(ctx context.Context, p base.LogicalPlan, op
|
||||
innerPlan = sort.Children()[0]
|
||||
apply.SetChildren(outerPlan, innerPlan)
|
||||
appendRemoveSortTraceStep(sort, opt)
|
||||
return s.optimize(ctx, p, opt)
|
||||
return s.Optimize(ctx, p, opt)
|
||||
}
|
||||
}
|
||||
NoOptimize:
|
||||
@ -378,7 +378,7 @@ NoOptimize:
|
||||
}
|
||||
newChildren := make([]base.LogicalPlan, 0, len(p.Children()))
|
||||
for _, child := range p.Children() {
|
||||
np, planChanged, err := s.optimize(ctx, child, opt)
|
||||
np, planChanged, err := s.Optimize(ctx, child, opt)
|
||||
if err != nil {
|
||||
return nil, planChanged, err
|
||||
}
|
||||
@ -388,7 +388,8 @@ NoOptimize:
|
||||
return p, planChanged, nil
|
||||
}
|
||||
|
||||
func (*decorrelateSolver) name() string {
|
||||
// Name implements base.LogicalOptRule.<1st> interface.
|
||||
func (*DecorrelateSolver) Name() string {
|
||||
return "decorrelate"
|
||||
}
|
||||
|
||||
|
||||
@ -26,8 +26,8 @@ import (
|
||||
"github.com/pingcap/tidb/pkg/planner/util/optimizetrace"
|
||||
)
|
||||
|
||||
// deriveTopNFromWindow pushes down the topN or limit. In the future we will remove the limit from `requiredProperty` in CBO phase.
|
||||
type deriveTopNFromWindow struct {
|
||||
// DeriveTopNFromWindow pushes down the topN or limit. In the future we will remove the limit from `requiredProperty` in CBO phase.
|
||||
type DeriveTopNFromWindow struct {
|
||||
}
|
||||
|
||||
func appendDerivedTopNTrace(topN base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) {
|
||||
@ -118,11 +118,13 @@ func windowIsTopN(p *LogicalSelection) (bool, uint64) {
|
||||
return false, 0
|
||||
}
|
||||
|
||||
func (*deriveTopNFromWindow) optimize(_ context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements base.LogicalOptRule.<0th> interface.
|
||||
func (*DeriveTopNFromWindow) Optimize(_ context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
return p.DeriveTopN(opt), planChanged, nil
|
||||
}
|
||||
|
||||
func (*deriveTopNFromWindow) name() string {
|
||||
// Name implements base.LogicalOptRule.<1st> interface.
|
||||
func (*DeriveTopNFromWindow) Name() string {
|
||||
return "derive_topn_from_window"
|
||||
}
|
||||
|
||||
@ -144,21 +144,21 @@ func eliminatePhysicalProjection(p base.PhysicalPlan) base.PhysicalPlan {
|
||||
// The projection eliminate in logical optimize will optimize the projection under the projection, window, agg
|
||||
// The projection eliminate in post optimize will optimize other projection
|
||||
|
||||
// For update stmt
|
||||
// ProjectionEliminator is for update stmt
|
||||
// The projection eliminate in logical optimize has been forbidden.
|
||||
// The projection eliminate in post optimize will optimize the projection under the projection, window, agg (the condition is same as logical optimize)
|
||||
type projectionEliminator struct {
|
||||
type ProjectionEliminator struct {
|
||||
}
|
||||
|
||||
// optimize implements the logicalOptRule interface.
|
||||
func (pe *projectionEliminator) optimize(_ context.Context, lp base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements the logicalOptRule interface.
|
||||
func (pe *ProjectionEliminator) Optimize(_ context.Context, lp base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
root := pe.eliminate(lp, make(map[string]*expression.Column), false, opt)
|
||||
return root, planChanged, nil
|
||||
}
|
||||
|
||||
// eliminate eliminates the redundant projection in a logical plan.
|
||||
func (pe *projectionEliminator) eliminate(p base.LogicalPlan, replace map[string]*expression.Column, canEliminate bool, opt *optimizetrace.LogicalOptimizeOp) base.LogicalPlan {
|
||||
func (pe *ProjectionEliminator) eliminate(p base.LogicalPlan, replace map[string]*expression.Column, canEliminate bool, opt *optimizetrace.LogicalOptimizeOp) base.LogicalPlan {
|
||||
// LogicalCTE's logical optimization is independent.
|
||||
if _, ok := p.(*LogicalCTE); ok {
|
||||
return p
|
||||
@ -233,7 +233,8 @@ func ReplaceColumnOfExpr(expr expression.Expression, proj *logicalop.LogicalProj
|
||||
return expr
|
||||
}
|
||||
|
||||
func (*projectionEliminator) name() string {
|
||||
// Name implements the logicalOptRule.<1st> interface.
|
||||
func (*ProjectionEliminator) Name() string {
|
||||
return "projection_eliminate"
|
||||
}
|
||||
|
||||
|
||||
@ -28,7 +28,8 @@ import (
|
||||
h "github.com/pingcap/tidb/pkg/util/hint"
|
||||
)
|
||||
|
||||
type gcSubstituter struct {
|
||||
// GcSubstituter is used to substitute the expression to indexed virtual generated column in where, group by, order by, and field clause.
|
||||
type GcSubstituter struct {
|
||||
}
|
||||
|
||||
// ExprColumnMap is used to store all expressions of indexed generated columns in a table,
|
||||
@ -36,12 +37,13 @@ type gcSubstituter struct {
|
||||
// thus we can substitute the expression in a query to an indexed generated column.
|
||||
type ExprColumnMap map[expression.Expression]*expression.Column
|
||||
|
||||
// Optimize implements base.LogicalOptRule.<0th> interface.
|
||||
// optimize try to replace the expression to indexed virtual generate column in where, group by, order by, and field clause
|
||||
// so that we can use the index on expression.
|
||||
// For example: select a+1 from t order by a+1, with a virtual generate column c as (a+1) and
|
||||
// an index on c. We need to replace a+1 with c so that we can use the index on c.
|
||||
// See also https://dev.mysql.com/doc/refman/8.0/en/generated-column-index-optimizations.html
|
||||
func (gc *gcSubstituter) optimize(ctx context.Context, lp base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
func (gc *GcSubstituter) Optimize(ctx context.Context, lp base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
exprToColumn := make(ExprColumnMap)
|
||||
collectGenerateColumn(lp, exprToColumn)
|
||||
@ -180,7 +182,7 @@ func substituteExpression(cond expression.Expression, lp base.LogicalPlan, exprT
|
||||
return changed
|
||||
}
|
||||
|
||||
func (gc *gcSubstituter) substitute(ctx context.Context, lp base.LogicalPlan, exprToColumn ExprColumnMap, opt *optimizetrace.LogicalOptimizeOp) base.LogicalPlan {
|
||||
func (gc *GcSubstituter) substitute(ctx context.Context, lp base.LogicalPlan, exprToColumn ExprColumnMap, opt *optimizetrace.LogicalOptimizeOp) base.LogicalPlan {
|
||||
var tp types.EvalType
|
||||
ectx := lp.SCtx().GetExprCtx().GetEvalCtx()
|
||||
switch x := lp.(type) {
|
||||
@ -232,6 +234,7 @@ func (gc *gcSubstituter) substitute(ctx context.Context, lp base.LogicalPlan, ex
|
||||
return lp
|
||||
}
|
||||
|
||||
func (*gcSubstituter) name() string {
|
||||
// Name implements base.LogicalOptRule.<1st> interface.
|
||||
func (*GcSubstituter) Name() string {
|
||||
return "generate_column_substitute"
|
||||
}
|
||||
|
||||
@ -28,7 +28,8 @@ import (
|
||||
"github.com/pingcap/tidb/pkg/util/set"
|
||||
)
|
||||
|
||||
type outerJoinEliminator struct {
|
||||
// OuterJoinEliminator is used to eliminate outer join.
|
||||
type OuterJoinEliminator struct {
|
||||
}
|
||||
|
||||
// tryToEliminateOuterJoin will eliminate outer join plan base on the following rules
|
||||
@ -38,7 +39,7 @@ type outerJoinEliminator struct {
|
||||
// 2. outer join elimination with duplicate agnostic aggregate functions: For example left outer join.
|
||||
// If the parent only use the columns from left table with 'distinct' label. The left outer join can
|
||||
// be eliminated.
|
||||
func (o *outerJoinEliminator) tryToEliminateOuterJoin(p *LogicalJoin, aggCols []*expression.Column, parentCols []*expression.Column, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
func (o *OuterJoinEliminator) tryToEliminateOuterJoin(p *LogicalJoin, aggCols []*expression.Column, parentCols []*expression.Column, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
var innerChildIdx int
|
||||
switch p.JoinType {
|
||||
case LeftOuterJoin:
|
||||
@ -96,7 +97,7 @@ func (o *outerJoinEliminator) tryToEliminateOuterJoin(p *LogicalJoin, aggCols []
|
||||
}
|
||||
|
||||
// extract join keys as a schema for inner child of a outer join
|
||||
func (*outerJoinEliminator) extractInnerJoinKeys(join *LogicalJoin, innerChildIdx int) *expression.Schema {
|
||||
func (*OuterJoinEliminator) extractInnerJoinKeys(join *LogicalJoin, innerChildIdx int) *expression.Schema {
|
||||
joinKeys := make([]*expression.Column, 0, len(join.EqualConditions))
|
||||
for _, eqCond := range join.EqualConditions {
|
||||
joinKeys = append(joinKeys, eqCond.GetArgs()[innerChildIdx].(*expression.Column))
|
||||
@ -121,7 +122,7 @@ func IsColsAllFromOuterTable(cols []*expression.Column, outerUniqueIDs set.Int64
|
||||
}
|
||||
|
||||
// check whether one of unique keys sets is contained by inner join keys
|
||||
func (*outerJoinEliminator) isInnerJoinKeysContainUniqueKey(innerPlan base.LogicalPlan, joinKeys *expression.Schema) (bool, error) {
|
||||
func (*OuterJoinEliminator) isInnerJoinKeysContainUniqueKey(innerPlan base.LogicalPlan, joinKeys *expression.Schema) (bool, error) {
|
||||
for _, keyInfo := range innerPlan.Schema().Keys {
|
||||
joinKeysContainKeyInfo := true
|
||||
for _, col := range keyInfo {
|
||||
@ -138,7 +139,7 @@ func (*outerJoinEliminator) isInnerJoinKeysContainUniqueKey(innerPlan base.Logic
|
||||
}
|
||||
|
||||
// check whether one of index sets is contained by inner join index
|
||||
func (*outerJoinEliminator) isInnerJoinKeysContainIndex(innerPlan base.LogicalPlan, joinKeys *expression.Schema) (bool, error) {
|
||||
func (*OuterJoinEliminator) isInnerJoinKeysContainIndex(innerPlan base.LogicalPlan, joinKeys *expression.Schema) (bool, error) {
|
||||
ds, ok := innerPlan.(*DataSource)
|
||||
if !ok {
|
||||
return false, nil
|
||||
@ -195,7 +196,7 @@ func GetDupAgnosticAggCols(
|
||||
return true, newAggCols
|
||||
}
|
||||
|
||||
func (o *outerJoinEliminator) doOptimize(p base.LogicalPlan, aggCols []*expression.Column, parentCols []*expression.Column, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, error) {
|
||||
func (o *OuterJoinEliminator) doOptimize(p base.LogicalPlan, aggCols []*expression.Column, parentCols []*expression.Column, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, error) {
|
||||
// CTE's logical optimization is independent.
|
||||
if _, ok := p.(*LogicalCTE); ok {
|
||||
return p, nil
|
||||
@ -249,13 +250,15 @@ func (o *outerJoinEliminator) doOptimize(p base.LogicalPlan, aggCols []*expressi
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (o *outerJoinEliminator) optimize(_ context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements base.LogicalOptRule.<0th> interface.
|
||||
func (o *OuterJoinEliminator) Optimize(_ context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
p, err := o.doOptimize(p, nil, nil, opt)
|
||||
return p, planChanged, err
|
||||
}
|
||||
|
||||
func (*outerJoinEliminator) name() string {
|
||||
// Name implements base.LogicalOptRule.<1st> interface.
|
||||
func (*OuterJoinEliminator) Name() string {
|
||||
return "outer_join_eliminate"
|
||||
}
|
||||
|
||||
|
||||
@ -212,7 +212,8 @@ func extractJoinGroup(p base.LogicalPlan) *joinGroupResult {
|
||||
}
|
||||
}
|
||||
|
||||
type joinReOrderSolver struct {
|
||||
// JoinReOrderSolver is used to reorder the join nodes in a logical plan.
|
||||
type JoinReOrderSolver struct {
|
||||
}
|
||||
|
||||
type jrNode struct {
|
||||
@ -225,7 +226,8 @@ type joinTypeWithExtMsg struct {
|
||||
outerBindCondition []expression.Expression
|
||||
}
|
||||
|
||||
func (s *joinReOrderSolver) optimize(_ context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements the base.LogicalOptRule.<0th> interface.
|
||||
func (s *JoinReOrderSolver) Optimize(_ context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
tracer := &joinReorderTrace{cost: map[string]float64{}, opt: opt}
|
||||
tracer.traceJoinReorder(p)
|
||||
@ -236,7 +238,7 @@ func (s *joinReOrderSolver) optimize(_ context.Context, p base.LogicalPlan, opt
|
||||
}
|
||||
|
||||
// optimizeRecursive recursively collects join groups and applies join reorder algorithm for each group.
|
||||
func (s *joinReOrderSolver) optimizeRecursive(ctx base.PlanContext, p base.LogicalPlan, tracer *joinReorderTrace) (base.LogicalPlan, error) {
|
||||
func (s *JoinReOrderSolver) optimizeRecursive(ctx base.PlanContext, p base.LogicalPlan, tracer *joinReorderTrace) (base.LogicalPlan, error) {
|
||||
if _, ok := p.(*LogicalCTE); ok {
|
||||
return p, nil
|
||||
}
|
||||
@ -687,7 +689,8 @@ func (*baseSingleGroupJoinOrderSolver) calcJoinCumCost(join base.LogicalPlan, lN
|
||||
return join.StatsInfo().RowCount + lNode.cumCost + rNode.cumCost
|
||||
}
|
||||
|
||||
func (*joinReOrderSolver) name() string {
|
||||
// Name implements the base.LogicalOptRule.<1st> interface.
|
||||
func (*JoinReOrderSolver) Name() string {
|
||||
return "join_reorder"
|
||||
}
|
||||
|
||||
|
||||
@ -32,20 +32,21 @@ import (
|
||||
"github.com/pingcap/tidb/pkg/util/ranger"
|
||||
)
|
||||
|
||||
// maxMinEliminator tries to eliminate max/min aggregate function.
|
||||
// MaxMinEliminator tries to eliminate max/min aggregate function.
|
||||
// For SQL like `select max(id) from t;`, we could optimize it to `select max(id) from (select id from t order by id desc limit 1 where id is not null) t;`.
|
||||
// For SQL like `select min(id) from t;`, we could optimize it to `select min(id) from (select id from t order by id limit 1 where id is not null) t;`.
|
||||
// For SQL like `select max(id), min(id) from t;`, we could optimize it to the cartesianJoin result of the two queries above if `id` has an index.
|
||||
type maxMinEliminator struct {
|
||||
type MaxMinEliminator struct {
|
||||
}
|
||||
|
||||
func (a *maxMinEliminator) optimize(_ context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements base.LogicalOptRule.<0th> interface.
|
||||
func (a *MaxMinEliminator) Optimize(_ context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
return a.eliminateMaxMin(p, opt), planChanged, nil
|
||||
}
|
||||
|
||||
// composeAggsByInnerJoin composes the scalar aggregations by cartesianJoin.
|
||||
func (*maxMinEliminator) composeAggsByInnerJoin(originAgg *LogicalAggregation, aggs []*LogicalAggregation, opt *optimizetrace.LogicalOptimizeOp) (plan base.LogicalPlan) {
|
||||
func (*MaxMinEliminator) composeAggsByInnerJoin(originAgg *LogicalAggregation, aggs []*LogicalAggregation, opt *optimizetrace.LogicalOptimizeOp) (plan base.LogicalPlan) {
|
||||
plan = aggs[0]
|
||||
sctx := plan.SCtx()
|
||||
joins := make([]*LogicalJoin, 0)
|
||||
@ -64,7 +65,7 @@ func (*maxMinEliminator) composeAggsByInnerJoin(originAgg *LogicalAggregation, a
|
||||
// checkColCanUseIndex checks whether there is an AccessPath satisfy the conditions:
|
||||
// 1. all of the selection's condition can be pushed down as AccessConds of the path.
|
||||
// 2. the path can keep order for `col` after pushing down the conditions.
|
||||
func (a *maxMinEliminator) checkColCanUseIndex(plan base.LogicalPlan, col *expression.Column, conditions []expression.Expression) bool {
|
||||
func (a *MaxMinEliminator) checkColCanUseIndex(plan base.LogicalPlan, col *expression.Column, conditions []expression.Expression) bool {
|
||||
switch p := plan.(type) {
|
||||
case *LogicalSelection:
|
||||
conditions = append(conditions, p.Conditions...)
|
||||
@ -108,7 +109,7 @@ func (a *maxMinEliminator) checkColCanUseIndex(plan base.LogicalPlan, col *expre
|
||||
|
||||
// cloneSubPlans shallow clones the subPlan. We only consider `Selection` and `DataSource` here,
|
||||
// because we have restricted the subPlan in `checkColCanUseIndex`.
|
||||
func (a *maxMinEliminator) cloneSubPlans(plan base.LogicalPlan) base.LogicalPlan {
|
||||
func (a *MaxMinEliminator) cloneSubPlans(plan base.LogicalPlan) base.LogicalPlan {
|
||||
switch p := plan.(type) {
|
||||
case *LogicalSelection:
|
||||
newConditions := make([]expression.Expression, len(p.Conditions))
|
||||
@ -141,7 +142,7 @@ func (a *maxMinEliminator) cloneSubPlans(plan base.LogicalPlan) base.LogicalPlan
|
||||
// `select max(a) from t` + `select min(a) from t` + `select max(b) from t`.
|
||||
// Then we check whether `a` and `b` have indices. If any of the used column has no index, we cannot eliminate
|
||||
// this aggregation.
|
||||
func (a *maxMinEliminator) splitAggFuncAndCheckIndices(agg *LogicalAggregation, opt *optimizetrace.LogicalOptimizeOp) (aggs []*LogicalAggregation, canEliminate bool) {
|
||||
func (a *MaxMinEliminator) splitAggFuncAndCheckIndices(agg *LogicalAggregation, opt *optimizetrace.LogicalOptimizeOp) (aggs []*LogicalAggregation, canEliminate bool) {
|
||||
for _, f := range agg.AggFuncs {
|
||||
// We must make sure the args of max/min is a simple single column.
|
||||
col, ok := f.Args[0].(*expression.Column)
|
||||
@ -173,7 +174,7 @@ func (a *maxMinEliminator) splitAggFuncAndCheckIndices(agg *LogicalAggregation,
|
||||
}
|
||||
|
||||
// eliminateSingleMaxMin tries to convert a single max/min to Limit+Sort operators.
|
||||
func (*maxMinEliminator) eliminateSingleMaxMin(agg *LogicalAggregation, opt *optimizetrace.LogicalOptimizeOp) *LogicalAggregation {
|
||||
func (*MaxMinEliminator) eliminateSingleMaxMin(agg *LogicalAggregation, opt *optimizetrace.LogicalOptimizeOp) *LogicalAggregation {
|
||||
f := agg.AggFuncs[0]
|
||||
child := agg.Children()[0]
|
||||
ctx := agg.SCtx()
|
||||
@ -214,7 +215,7 @@ func (*maxMinEliminator) eliminateSingleMaxMin(agg *LogicalAggregation, opt *opt
|
||||
}
|
||||
|
||||
// eliminateMaxMin tries to convert max/min to Limit+Sort operators.
|
||||
func (a *maxMinEliminator) eliminateMaxMin(p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) base.LogicalPlan {
|
||||
func (a *MaxMinEliminator) eliminateMaxMin(p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) base.LogicalPlan {
|
||||
// CTE's logical optimization is indenpent.
|
||||
if _, ok := p.(*LogicalCTE); ok {
|
||||
return p
|
||||
@ -260,7 +261,8 @@ func (a *maxMinEliminator) eliminateMaxMin(p base.LogicalPlan, opt *optimizetrac
|
||||
return p
|
||||
}
|
||||
|
||||
func (*maxMinEliminator) name() string {
|
||||
// Name implements base.LogicalOptRule.<1st> interface.
|
||||
func (*MaxMinEliminator) Name() string {
|
||||
return "max_min_eliminate"
|
||||
}
|
||||
|
||||
|
||||
@ -35,10 +35,11 @@ func mergeOnClausePredicates(p *LogicalJoin, predicates []expression.Expression)
|
||||
return combinedCond
|
||||
}
|
||||
|
||||
// convertOuterToInnerJoin converts outer to inner joins if the unmtaching rows are filtered.
|
||||
type convertOuterToInnerJoin struct {
|
||||
// ConvertOuterToInnerJoin converts outer to inner joins if the unmtaching rows are filtered.
|
||||
type ConvertOuterToInnerJoin struct {
|
||||
}
|
||||
|
||||
// Optimize implements base.LogicalOptRule.<0th> interface.
|
||||
// convertOuterToInnerJoin is refactoring of the outer to inner join logic that used to be part of predicate push down.
|
||||
// The rewrite passes down predicates from selection (WHERE clause) and join predicates (ON clause).
|
||||
// All nodes except LogicalJoin are pass through where the rewrite is done for the child and nothing for the node itself.
|
||||
@ -50,7 +51,7 @@ type convertOuterToInnerJoin struct {
|
||||
// - For inner/semi joins, the ON clause can be applied on both children
|
||||
// - For anti semi joins, ON clause applied only on left side
|
||||
// - For all other cases, do not pass ON clause.
|
||||
func (*convertOuterToInnerJoin) optimize(_ context.Context, p base.LogicalPlan, _ *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
func (*ConvertOuterToInnerJoin) Optimize(_ context.Context, p base.LogicalPlan, _ *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
return p.ConvertOuterToInnerJoin(nil), planChanged, nil
|
||||
}
|
||||
@ -59,6 +60,7 @@ func (*convertOuterToInnerJoin) optimize(_ context.Context, p base.LogicalPlan,
|
||||
// Also, predicates involving aggregate expressions are not null filtering. IsNullReject always returns
|
||||
// false for those cases.
|
||||
|
||||
func (*convertOuterToInnerJoin) name() string {
|
||||
// Name implements base.LogicalOptRule.<1st> interface.
|
||||
func (*ConvertOuterToInnerJoin) Name() string {
|
||||
return "convert_outer_to_inner_joins"
|
||||
}
|
||||
|
||||
@ -48,7 +48,7 @@ import (
|
||||
// FullRange represent used all partitions.
|
||||
const FullRange = -1
|
||||
|
||||
// partitionProcessor rewrites the ast for table partition.
|
||||
// PartitionProcessor rewrites the ast for table partition.
|
||||
// Used by static partition prune mode.
|
||||
/*
|
||||
// create table t (id int) partition by range (id)
|
||||
@ -62,16 +62,22 @@ const FullRange = -1
|
||||
// select * from p2 where id < 20
|
||||
// select * from p3 where id < 30)
|
||||
*/
|
||||
// partitionProcessor is here because it's easier to prune partition after predicate push down.
|
||||
type partitionProcessor struct{}
|
||||
// PartitionProcessor is here because it's easier to prune partition after predicate push down.
|
||||
type PartitionProcessor struct{}
|
||||
|
||||
func (s *partitionProcessor) optimize(_ context.Context, lp base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements the LogicalOptRule.<0th> interface.
|
||||
func (s *PartitionProcessor) Optimize(_ context.Context, lp base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
p, err := s.rewriteDataSource(lp, opt)
|
||||
return p, planChanged, err
|
||||
}
|
||||
|
||||
func (s *partitionProcessor) rewriteDataSource(lp base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, error) {
|
||||
// Name implements the LogicalOptRule.<1st> interface.
|
||||
func (*PartitionProcessor) Name() string {
|
||||
return "partition_processor"
|
||||
}
|
||||
|
||||
func (s *PartitionProcessor) rewriteDataSource(lp base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, error) {
|
||||
// Assert there will not be sel -> sel in the ast.
|
||||
switch p := lp.(type) {
|
||||
case *DataSource:
|
||||
@ -148,7 +154,7 @@ func getPartColumnsForHashPartition(hashExpr expression.Expression) ([]*expressi
|
||||
return partCols, colLen
|
||||
}
|
||||
|
||||
func (s *partitionProcessor) getUsedHashPartitions(ctx base.PlanContext,
|
||||
func (s *PartitionProcessor) getUsedHashPartitions(ctx base.PlanContext,
|
||||
tbl table.Table, partitionNames []model.CIStr, columns []*expression.Column,
|
||||
conds []expression.Expression, names types.NameSlice) ([]int, error) {
|
||||
pi := tbl.Meta().Partition
|
||||
@ -266,7 +272,7 @@ func (s *partitionProcessor) getUsedHashPartitions(ctx base.PlanContext,
|
||||
return used, nil
|
||||
}
|
||||
|
||||
func (s *partitionProcessor) getUsedKeyPartitions(ctx base.PlanContext,
|
||||
func (s *PartitionProcessor) getUsedKeyPartitions(ctx base.PlanContext,
|
||||
tbl table.Table, partitionNames []model.CIStr, columns []*expression.Column,
|
||||
conds []expression.Expression, _ types.NameSlice) ([]int, error) {
|
||||
pi := tbl.Meta().Partition
|
||||
@ -374,7 +380,7 @@ func (s *partitionProcessor) getUsedKeyPartitions(ctx base.PlanContext,
|
||||
}
|
||||
|
||||
// getUsedPartitions is used to get used partitions for hash or key partition tables
|
||||
func (s *partitionProcessor) getUsedPartitions(ctx base.PlanContext, tbl table.Table,
|
||||
func (s *PartitionProcessor) getUsedPartitions(ctx base.PlanContext, tbl table.Table,
|
||||
partitionNames []model.CIStr, columns []*expression.Column, conds []expression.Expression,
|
||||
names types.NameSlice, partType model.PartitionType) ([]int, error) {
|
||||
if partType == model.PartitionTypeHash {
|
||||
@ -385,7 +391,7 @@ func (s *partitionProcessor) getUsedPartitions(ctx base.PlanContext, tbl table.T
|
||||
|
||||
// findUsedPartitions is used to get used partitions for hash or key partition tables.
|
||||
// The first returning is the used partition index set pruned by `conds`.
|
||||
func (s *partitionProcessor) findUsedPartitions(ctx base.PlanContext,
|
||||
func (s *PartitionProcessor) findUsedPartitions(ctx base.PlanContext,
|
||||
tbl table.Table, partitionNames []model.CIStr, conds []expression.Expression,
|
||||
columns []*expression.Column, names types.NameSlice) ([]int, error) {
|
||||
pi := tbl.Meta().Partition
|
||||
@ -408,7 +414,7 @@ func (s *partitionProcessor) findUsedPartitions(ctx base.PlanContext,
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (s *partitionProcessor) convertToIntSlice(or partitionRangeOR, pi *model.PartitionInfo, partitionNames []model.CIStr) []int {
|
||||
func (s *PartitionProcessor) convertToIntSlice(or partitionRangeOR, pi *model.PartitionInfo, partitionNames []model.CIStr) []int {
|
||||
if len(or) == 1 && or[0].start == 0 && or[0].end == len(pi.Definitions) {
|
||||
if len(partitionNames) == 0 {
|
||||
if len(pi.Definitions) == 1 {
|
||||
@ -442,7 +448,7 @@ func convertToRangeOr(used []int, pi *model.PartitionInfo) partitionRangeOR {
|
||||
}
|
||||
|
||||
// pruneHashOrKeyPartition is used to prune hash or key partition tables
|
||||
func (s *partitionProcessor) pruneHashOrKeyPartition(ctx base.PlanContext, tbl table.Table, partitionNames []model.CIStr,
|
||||
func (s *PartitionProcessor) pruneHashOrKeyPartition(ctx base.PlanContext, tbl table.Table, partitionNames []model.CIStr,
|
||||
conds []expression.Expression, columns []*expression.Column, names types.NameSlice) ([]int, error) {
|
||||
used, err := s.findUsedPartitions(ctx, tbl, partitionNames, conds, columns, names)
|
||||
if err != nil {
|
||||
@ -454,7 +460,7 @@ func (s *partitionProcessor) pruneHashOrKeyPartition(ctx base.PlanContext, tbl t
|
||||
// reconstructTableColNames reconstructs FieldsNames according to ds.TblCols.
|
||||
// ds.names may not match ds.TblCols since ds.names is pruned while ds.TblCols contains all original columns.
|
||||
// please see https://github.com/pingcap/tidb/issues/22635 for more details.
|
||||
func (*partitionProcessor) reconstructTableColNames(ds *DataSource) ([]*types.FieldName, error) {
|
||||
func (*PartitionProcessor) reconstructTableColNames(ds *DataSource) ([]*types.FieldName, error) {
|
||||
names := make([]*types.FieldName, 0, len(ds.TblCols))
|
||||
// Use DeletableCols to get all the columns.
|
||||
colsInfo := ds.table.DeletableCols()
|
||||
@ -498,7 +504,7 @@ func (*partitionProcessor) reconstructTableColNames(ds *DataSource) ([]*types.Fi
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func (s *partitionProcessor) processHashOrKeyPartition(ds *DataSource, pi *model.PartitionInfo, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, error) {
|
||||
func (s *PartitionProcessor) processHashOrKeyPartition(ds *DataSource, pi *model.PartitionInfo, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, error) {
|
||||
names, err := s.reconstructTableColNames(ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -520,7 +526,7 @@ func (s *partitionProcessor) processHashOrKeyPartition(ds *DataSource, pi *model
|
||||
|
||||
// listPartitionPruner uses to prune partition for list partition.
|
||||
type listPartitionPruner struct {
|
||||
*partitionProcessor
|
||||
*PartitionProcessor
|
||||
ctx base.PlanContext
|
||||
pi *model.PartitionInfo
|
||||
partitionNames []model.CIStr
|
||||
@ -528,7 +534,7 @@ type listPartitionPruner struct {
|
||||
listPrune *tables.ForListPruning
|
||||
}
|
||||
|
||||
func newListPartitionPruner(ctx base.PlanContext, tbl table.Table, partitionNames []model.CIStr, s *partitionProcessor, pruneList *tables.ForListPruning, columns []*expression.Column) *listPartitionPruner {
|
||||
func newListPartitionPruner(ctx base.PlanContext, tbl table.Table, partitionNames []model.CIStr, s *PartitionProcessor, pruneList *tables.ForListPruning, columns []*expression.Column) *listPartitionPruner {
|
||||
pruneList = pruneList.Clone()
|
||||
for i := range pruneList.PruneExprCols {
|
||||
for j := range columns {
|
||||
@ -549,7 +555,7 @@ func newListPartitionPruner(ctx base.PlanContext, tbl table.Table, partitionName
|
||||
fullRange := make(map[int]struct{})
|
||||
fullRange[FullRange] = struct{}{}
|
||||
return &listPartitionPruner{
|
||||
partitionProcessor: s,
|
||||
PartitionProcessor: s,
|
||||
ctx: ctx,
|
||||
pi: tbl.Meta().Partition,
|
||||
partitionNames: partitionNames,
|
||||
@ -783,7 +789,7 @@ func (l *listPartitionPruner) findUsedListPartitions(conds []expression.Expressi
|
||||
return used, nil
|
||||
}
|
||||
|
||||
func (s *partitionProcessor) findUsedListPartitions(ctx base.PlanContext, tbl table.Table, partitionNames []model.CIStr,
|
||||
func (s *PartitionProcessor) findUsedListPartitions(ctx base.PlanContext, tbl table.Table, partitionNames []model.CIStr,
|
||||
conds []expression.Expression, columns []*expression.Column) ([]int, error) {
|
||||
pi := tbl.Meta().Partition
|
||||
partExpr := tbl.(partitionTable).PartitionExpr()
|
||||
@ -811,7 +817,7 @@ func (s *partitionProcessor) findUsedListPartitions(ctx base.PlanContext, tbl ta
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (s *partitionProcessor) pruneListPartition(ctx base.PlanContext, tbl table.Table, partitionNames []model.CIStr,
|
||||
func (s *PartitionProcessor) pruneListPartition(ctx base.PlanContext, tbl table.Table, partitionNames []model.CIStr,
|
||||
conds []expression.Expression, columns []*expression.Column) ([]int, error) {
|
||||
used, err := s.findUsedListPartitions(ctx, tbl, partitionNames, conds, columns)
|
||||
if err != nil {
|
||||
@ -820,7 +826,7 @@ func (s *partitionProcessor) pruneListPartition(ctx base.PlanContext, tbl table.
|
||||
return used, nil
|
||||
}
|
||||
|
||||
func (s *partitionProcessor) prune(ds *DataSource, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, error) {
|
||||
func (s *PartitionProcessor) prune(ds *DataSource, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, error) {
|
||||
pi := ds.TableInfo.GetPartitionInfo()
|
||||
if pi == nil {
|
||||
return ds, nil
|
||||
@ -849,7 +855,7 @@ func (s *partitionProcessor) prune(ds *DataSource, opt *optimizetrace.LogicalOpt
|
||||
}
|
||||
|
||||
// findByName checks whether object name exists in list.
|
||||
func (*partitionProcessor) findByName(partitionNames []model.CIStr, partitionName string) bool {
|
||||
func (*PartitionProcessor) findByName(partitionNames []model.CIStr, partitionName string) bool {
|
||||
for _, s := range partitionNames {
|
||||
if s.L == partitionName {
|
||||
return true
|
||||
@ -858,10 +864,6 @@ func (*partitionProcessor) findByName(partitionNames []model.CIStr, partitionNam
|
||||
return false
|
||||
}
|
||||
|
||||
func (*partitionProcessor) name() string {
|
||||
return "partition_processor"
|
||||
}
|
||||
|
||||
type lessThanDataInt struct {
|
||||
data []int64
|
||||
unsigned bool
|
||||
@ -997,7 +999,7 @@ func intersectionRange(start, end, newStart, newEnd int) (s int, e int) {
|
||||
return s, e
|
||||
}
|
||||
|
||||
func (s *partitionProcessor) pruneRangePartition(ctx base.PlanContext, pi *model.PartitionInfo, tbl table.PartitionedTable, conds []expression.Expression,
|
||||
func (s *PartitionProcessor) pruneRangePartition(ctx base.PlanContext, pi *model.PartitionInfo, tbl table.PartitionedTable, conds []expression.Expression,
|
||||
columns []*expression.Column, names types.NameSlice) (partitionRangeOR, error) {
|
||||
partExpr := tbl.(partitionTable).PartitionExpr()
|
||||
|
||||
@ -1033,7 +1035,7 @@ func (s *partitionProcessor) pruneRangePartition(ctx base.PlanContext, pi *model
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *partitionProcessor) processRangePartition(ds *DataSource, pi *model.PartitionInfo, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, error) {
|
||||
func (s *PartitionProcessor) processRangePartition(ds *DataSource, pi *model.PartitionInfo, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, error) {
|
||||
used, err := s.pruneRangePartition(ds.SCtx(), pi, ds.table.(table.PartitionedTable), ds.AllConds, ds.TblCols, ds.OutputNames())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -1041,7 +1043,7 @@ func (s *partitionProcessor) processRangePartition(ds *DataSource, pi *model.Par
|
||||
return s.makeUnionAllChildren(ds, pi, used, opt)
|
||||
}
|
||||
|
||||
func (s *partitionProcessor) processListPartition(ds *DataSource, pi *model.PartitionInfo, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, error) {
|
||||
func (s *PartitionProcessor) processListPartition(ds *DataSource, pi *model.PartitionInfo, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, error) {
|
||||
used, err := s.pruneListPartition(ds.SCtx(), ds.table, ds.PartitionNames, ds.AllConds, ds.TblCols)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -1711,7 +1713,7 @@ func pruneUseBinarySearch(lessThan lessThanDataInt, data dataForPrune) (start in
|
||||
return start, end
|
||||
}
|
||||
|
||||
func (*partitionProcessor) resolveAccessPaths(ds *DataSource) error {
|
||||
func (*PartitionProcessor) resolveAccessPaths(ds *DataSource) error {
|
||||
possiblePaths, err := getPossibleAccessPaths(
|
||||
ds.SCtx(), &h.PlanHints{IndexMergeHintList: ds.IndexMergeHints, IndexHintList: ds.IndexHints},
|
||||
ds.AstIndexHints, ds.table, ds.DBName, ds.TableInfo.Name, ds.IsForUpdateRead, true)
|
||||
@ -1726,7 +1728,7 @@ func (*partitionProcessor) resolveAccessPaths(ds *DataSource) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *partitionProcessor) resolveOptimizeHint(ds *DataSource, partitionName model.CIStr) error {
|
||||
func (s *PartitionProcessor) resolveOptimizeHint(ds *DataSource, partitionName model.CIStr) error {
|
||||
// index hint
|
||||
if len(ds.IndexHints) > 0 {
|
||||
newIndexHint := make([]h.HintedIndex, 0, len(ds.IndexHints))
|
||||
@ -1811,7 +1813,7 @@ func appendWarnForUnknownPartitions(ctx base.PlanContext, hintName string, unkno
|
||||
ctx.GetSessionVars().StmtCtx.SetHintWarningFromError(warning)
|
||||
}
|
||||
|
||||
func (*partitionProcessor) checkHintsApplicable(ds *DataSource, partitionSet set.StringSet) {
|
||||
func (*PartitionProcessor) checkHintsApplicable(ds *DataSource, partitionSet set.StringSet) {
|
||||
for _, idxHint := range ds.IndexHints {
|
||||
unknownPartitions := checkTableHintsApplicableForPartition(idxHint.Partitions, partitionSet)
|
||||
appendWarnForUnknownPartitions(ds.SCtx(), h.Restore2IndexHint(idxHint.HintTypeString(), idxHint), unknownPartitions)
|
||||
@ -1826,7 +1828,7 @@ func (*partitionProcessor) checkHintsApplicable(ds *DataSource, partitionSet set
|
||||
appendWarnForUnknownPartitions(ds.SCtx(), h.HintReadFromStorage, unknownPartitions)
|
||||
}
|
||||
|
||||
func (s *partitionProcessor) makeUnionAllChildren(ds *DataSource, pi *model.PartitionInfo, or partitionRangeOR, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, error) {
|
||||
func (s *PartitionProcessor) makeUnionAllChildren(ds *DataSource, pi *model.PartitionInfo, or partitionRangeOR, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, error) {
|
||||
children := make([]base.LogicalPlan, 0, len(pi.Definitions))
|
||||
partitionNameSet := make(set.StringSet)
|
||||
usedDefinition := make(map[int64]model.PartitionDefinition)
|
||||
@ -1883,7 +1885,7 @@ func (s *partitionProcessor) makeUnionAllChildren(ds *DataSource, pi *model.Part
|
||||
return unionAll, nil
|
||||
}
|
||||
|
||||
func (*partitionProcessor) pruneRangeColumnsPartition(ctx base.PlanContext, conds []expression.Expression, pi *model.PartitionInfo, pe *tables.PartitionExpr, columns []*expression.Column) (partitionRangeOR, error) {
|
||||
func (*PartitionProcessor) pruneRangeColumnsPartition(ctx base.PlanContext, conds []expression.Expression, pi *model.PartitionInfo, pe *tables.PartitionExpr, columns []*expression.Column) (partitionRangeOR, error) {
|
||||
result := fullRange(len(pi.Definitions))
|
||||
|
||||
if len(pi.Columns) < 1 {
|
||||
|
||||
@ -30,7 +30,8 @@ import (
|
||||
"github.com/pingcap/tidb/pkg/util/ranger"
|
||||
)
|
||||
|
||||
type ppdSolver struct{}
|
||||
// PPDSolver stands for Predicate Push Down.
|
||||
type PPDSolver struct{}
|
||||
|
||||
// exprPrefixAdder is the wrapper struct to add tidb_shard(x) = val for `OrigConds`
|
||||
// `cols` is the index columns for a unique shard index
|
||||
@ -41,7 +42,8 @@ type exprPrefixAdder struct {
|
||||
lengths []int
|
||||
}
|
||||
|
||||
func (*ppdSolver) optimize(_ context.Context, lp base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements base.LogicalOptRule.<0th> interface.
|
||||
func (*PPDSolver) Optimize(_ context.Context, lp base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
_, p := lp.PredicatePushDown(nil, opt)
|
||||
return p, planChanged, nil
|
||||
@ -195,7 +197,8 @@ func DeleteTrueExprs(p base.LogicalPlan, conds []expression.Expression) []expres
|
||||
return newConds
|
||||
}
|
||||
|
||||
func (*ppdSolver) name() string {
|
||||
// Name implements base.LogicalOptRule.<1st> interface.
|
||||
func (*PPDSolver) Name() string {
|
||||
return "predicate_push_down"
|
||||
}
|
||||
|
||||
|
||||
@ -24,9 +24,9 @@ import (
|
||||
"github.com/pingcap/tidb/pkg/planner/util/optimizetrace"
|
||||
)
|
||||
|
||||
// predicateSimplification consolidates different predcicates on a column and its equivalence classes. Initial out is for
|
||||
// PredicateSimplification consolidates different predcicates on a column and its equivalence classes. Initial out is for
|
||||
// in-list and not equal list intersection.
|
||||
type predicateSimplification struct {
|
||||
type PredicateSimplification struct {
|
||||
}
|
||||
|
||||
type predicateType = byte
|
||||
@ -65,7 +65,8 @@ func findPredicateType(expr expression.Expression) (*expression.Column, predicat
|
||||
return nil, otherPredicate
|
||||
}
|
||||
|
||||
func (*predicateSimplification) optimize(_ context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements base.LogicalOptRule.<0th> interface.
|
||||
func (*PredicateSimplification) Optimize(_ context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
return p.PredicateSimplification(opt), planChanged, nil
|
||||
}
|
||||
@ -146,6 +147,7 @@ func applyPredicateSimplification(sctx base.PlanContext, predicates []expression
|
||||
return newValues
|
||||
}
|
||||
|
||||
func (*predicateSimplification) name() string {
|
||||
// Name implements base.LogicalOptRule.<1st> interface.
|
||||
func (*PredicateSimplification) Name() string {
|
||||
return "predicate_simplification"
|
||||
}
|
||||
|
||||
@ -22,19 +22,22 @@ import (
|
||||
"github.com/pingcap/tidb/pkg/planner/util/optimizetrace"
|
||||
)
|
||||
|
||||
type pushDownSequenceSolver struct {
|
||||
// PushDownSequenceSolver is used to push down sequence.
|
||||
type PushDownSequenceSolver struct {
|
||||
}
|
||||
|
||||
func (*pushDownSequenceSolver) name() string {
|
||||
// Name implements the base.LogicalOptRule.<1st> interface.
|
||||
func (*PushDownSequenceSolver) Name() string {
|
||||
return "push_down_sequence"
|
||||
}
|
||||
|
||||
func (pdss *pushDownSequenceSolver) optimize(_ context.Context, lp base.LogicalPlan, _ *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements the base.LogicalOptRule.<0th> interface.
|
||||
func (pdss *PushDownSequenceSolver) Optimize(_ context.Context, lp base.LogicalPlan, _ *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
return pdss.recursiveOptimize(nil, lp), planChanged, nil
|
||||
}
|
||||
|
||||
func (pdss *pushDownSequenceSolver) recursiveOptimize(pushedSequence *logicalop.LogicalSequence, lp base.LogicalPlan) base.LogicalPlan {
|
||||
func (pdss *PushDownSequenceSolver) recursiveOptimize(pushedSequence *logicalop.LogicalSequence, lp base.LogicalPlan) base.LogicalPlan {
|
||||
_, ok := lp.(*logicalop.LogicalSequence)
|
||||
if !ok && pushedSequence == nil {
|
||||
newChildren := make([]base.LogicalPlan, 0, len(lp.Children()))
|
||||
|
||||
@ -51,10 +51,11 @@ import (
|
||||
// to achieve this similar effect, put it in the last logical optimizing phase is much
|
||||
// more reasonable.
|
||||
|
||||
// resolveExpand generating Expand projection list when all the logical optimization is done.
|
||||
type resolveExpand struct {
|
||||
// ResolveExpand generating Expand projection list when all the logical optimization is done.
|
||||
type ResolveExpand struct {
|
||||
}
|
||||
|
||||
// Optimize implements the base.LogicalOptRule.<0th> interface.
|
||||
// By now, rollup syntax will build a LogicalExpand from bottom up. In LogicalExpand itself, its schema out should be 3 parts:
|
||||
//
|
||||
// +---------------------------------------------------------------------+
|
||||
@ -75,7 +76,7 @@ type resolveExpand struct {
|
||||
// (upper required) (grouping sets columns appended)
|
||||
//
|
||||
// Expand operator itself is kind like a projection, while difference is that it has a multi projection list, named as leveled projection.
|
||||
func (*resolveExpand) optimize(_ context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
func (*ResolveExpand) Optimize(_ context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
// As you see, Expand's leveled projection should be built after all column-prune is done. So we just make generating-leveled-projection
|
||||
// as the last rule of logical optimization, which is more clear. (spark has column prune action before building expand)
|
||||
@ -83,7 +84,8 @@ func (*resolveExpand) optimize(_ context.Context, p base.LogicalPlan, opt *optim
|
||||
return newLogicalPlan, planChanged, err
|
||||
}
|
||||
|
||||
func (*resolveExpand) name() string {
|
||||
// Name implements the base.LogicalOptRule.<1st> interface.
|
||||
func (*ResolveExpand) Name() string {
|
||||
return "resolve_expand"
|
||||
}
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ import (
|
||||
)
|
||||
|
||||
/*
|
||||
resultReorder reorder query results.
|
||||
ResultReorder reorder query results.
|
||||
NOTE: it's not a common rule for all queries, it's specially implemented for a few customers.
|
||||
|
||||
Results of some queries are not ordered, for example:
|
||||
@ -39,10 +39,11 @@ This rule reorders results by modifying or injecting a Sort operator:
|
||||
2.1. if it's a Sort, update it by appending all output columns into its order-by list,
|
||||
2.2. otherwise, inject a new Sort upon this operator.
|
||||
*/
|
||||
type resultReorder struct {
|
||||
type ResultReorder struct {
|
||||
}
|
||||
|
||||
func (rs *resultReorder) optimize(_ context.Context, lp base.LogicalPlan, _ *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements base.LogicalOptRule.<0th> interface.
|
||||
func (rs *ResultReorder) Optimize(_ context.Context, lp base.LogicalPlan, _ *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
ordered := rs.completeSort(lp)
|
||||
if !ordered {
|
||||
@ -51,7 +52,7 @@ func (rs *resultReorder) optimize(_ context.Context, lp base.LogicalPlan, _ *opt
|
||||
return lp, planChanged, nil
|
||||
}
|
||||
|
||||
func (rs *resultReorder) completeSort(lp base.LogicalPlan) bool {
|
||||
func (rs *ResultReorder) completeSort(lp base.LogicalPlan) bool {
|
||||
if rs.isInputOrderKeeper(lp) {
|
||||
if len(lp.Children()) == 0 {
|
||||
return true
|
||||
@ -79,7 +80,7 @@ func (rs *resultReorder) completeSort(lp base.LogicalPlan) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (rs *resultReorder) injectSort(lp base.LogicalPlan) base.LogicalPlan {
|
||||
func (rs *ResultReorder) injectSort(lp base.LogicalPlan) base.LogicalPlan {
|
||||
if rs.isInputOrderKeeper(lp) {
|
||||
lp.SetChildren(rs.injectSort(lp.Children()[0]))
|
||||
return lp
|
||||
@ -100,7 +101,7 @@ func (rs *resultReorder) injectSort(lp base.LogicalPlan) base.LogicalPlan {
|
||||
return sort
|
||||
}
|
||||
|
||||
func (*resultReorder) isInputOrderKeeper(lp base.LogicalPlan) bool {
|
||||
func (*ResultReorder) isInputOrderKeeper(lp base.LogicalPlan) bool {
|
||||
switch lp.(type) {
|
||||
case *LogicalSelection, *logicalop.LogicalProjection, *logicalop.LogicalLimit, *logicalop.LogicalTableDual:
|
||||
return true
|
||||
@ -109,7 +110,7 @@ func (*resultReorder) isInputOrderKeeper(lp base.LogicalPlan) bool {
|
||||
}
|
||||
|
||||
// extractHandleCols does the best effort to get the handle column.
|
||||
func (rs *resultReorder) extractHandleCol(lp base.LogicalPlan) *expression.Column {
|
||||
func (rs *ResultReorder) extractHandleCol(lp base.LogicalPlan) *expression.Column {
|
||||
switch x := lp.(type) {
|
||||
case *LogicalSelection, *logicalop.LogicalLimit:
|
||||
handleCol := rs.extractHandleCol(lp.Children()[0])
|
||||
@ -133,6 +134,7 @@ func (rs *resultReorder) extractHandleCol(lp base.LogicalPlan) *expression.Colum
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*resultReorder) name() string {
|
||||
// Name implements base.LogicalOptRule.<1st> interface.
|
||||
func (*ResultReorder) Name() string {
|
||||
return "result_reorder"
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ import (
|
||||
h "github.com/pingcap/tidb/pkg/util/hint"
|
||||
)
|
||||
|
||||
// semiJoinRewriter rewrites semi join to inner join with aggregation.
|
||||
// SemiJoinRewriter rewrites semi join to inner join with aggregation.
|
||||
// Note: This rewriter is only used for exists subquery.
|
||||
// And it also requires the hint `SEMI_JOIN_REWRITE` to be set.
|
||||
// For example:
|
||||
@ -36,20 +36,22 @@ import (
|
||||
// will be rewriten to:
|
||||
//
|
||||
// select * from t join (select a from s group by a) s on t.a = s.a;
|
||||
type semiJoinRewriter struct {
|
||||
type SemiJoinRewriter struct {
|
||||
}
|
||||
|
||||
func (smj *semiJoinRewriter) optimize(_ context.Context, p base.LogicalPlan, _ *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements base.LogicalOptRule.<0th> interface.
|
||||
func (smj *SemiJoinRewriter) Optimize(_ context.Context, p base.LogicalPlan, _ *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
newLogicalPlan, err := smj.recursivePlan(p)
|
||||
return newLogicalPlan, planChanged, err
|
||||
}
|
||||
|
||||
func (*semiJoinRewriter) name() string {
|
||||
// Name implements base.LogicalOptRule.<1st> interface.
|
||||
func (*SemiJoinRewriter) Name() string {
|
||||
return "semi_join_rewrite"
|
||||
}
|
||||
|
||||
func (smj *semiJoinRewriter) recursivePlan(p base.LogicalPlan) (base.LogicalPlan, error) {
|
||||
func (smj *SemiJoinRewriter) recursivePlan(p base.LogicalPlan) (base.LogicalPlan, error) {
|
||||
if _, ok := p.(*LogicalCTE); ok {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
@ -25,11 +25,12 @@ import (
|
||||
"github.com/pingcap/tidb/pkg/planner/util/optimizetrace"
|
||||
)
|
||||
|
||||
// pushDownTopNOptimizer pushes down the topN or limit. In the future we will remove the limit from `requiredProperty` in CBO phase.
|
||||
type pushDownTopNOptimizer struct {
|
||||
// PushDownTopNOptimizer pushes down the topN or limit. In the future we will remove the limit from `requiredProperty` in CBO phase.
|
||||
type PushDownTopNOptimizer struct {
|
||||
}
|
||||
|
||||
func (*pushDownTopNOptimizer) optimize(_ context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
// Optimize implements the base.LogicalOptRule.<0th> interface.
|
||||
func (*PushDownTopNOptimizer) Optimize(_ context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
|
||||
planChanged := false
|
||||
return p.PushDownTopN(nil, opt), planChanged, nil
|
||||
}
|
||||
@ -52,7 +53,8 @@ func pushDownTopNForBaseLogicalPlan(lp base.LogicalPlan, topNLogicalPlan base.Lo
|
||||
return p
|
||||
}
|
||||
|
||||
func (*pushDownTopNOptimizer) name() string {
|
||||
// Name implements the base.LogicalOptRule.<1st> interface.
|
||||
func (*PushDownTopNOptimizer) Name() string {
|
||||
return "topn_push_down"
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user