Skip to content

Commit 3d82fc5

Browse files
authored
executor,planner: Relax projection column expression push down check conditions (#52502)
close #52501
1 parent bb84d1f commit 3d82fc5

File tree

7 files changed

+57
-268
lines changed

7 files changed

+57
-268
lines changed

pkg/expression/expr_to_pb.go

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,25 @@ func ExpressionsToPBList(ctx EvalContext, exprs []Expression, client kv.Client)
4646
return
4747
}
4848

49+
// ProjectionExpressionsToPBList converts PhysicalProjection's expressions to tipb.Expr list for new plan.
50+
// It doesn't check type for top level column expression, since top level column expression doesn't imply any calculations
51+
func ProjectionExpressionsToPBList(ctx EvalContext, exprs []Expression, client kv.Client) (pbExpr []*tipb.Expr, err error) {
52+
pc := PbConverter{client: client, ctx: ctx}
53+
for _, expr := range exprs {
54+
var v *tipb.Expr
55+
if column, ok := expr.(*Column); ok {
56+
v = pc.columnToPBExpr(column, false)
57+
} else {
58+
v = pc.ExprToPB(expr)
59+
}
60+
if v == nil {
61+
return nil, plannererrors.ErrInternal.GenWithStack("expression %v cannot be pushed down", expr)
62+
}
63+
pbExpr = append(pbExpr, v)
64+
}
65+
return
66+
}
67+
4968
// PbConverter supplies methods to convert TiDB expressions to TiPB.
5069
type PbConverter struct {
5170
client kv.Client
@@ -69,7 +88,7 @@ func (pc PbConverter) ExprToPB(expr Expression) *tipb.Expr {
6988
case *CorrelatedColumn:
7089
return pc.conOrCorColToPBExpr(expr)
7190
case *Column:
72-
return pc.columnToPBExpr(x)
91+
return pc.columnToPBExpr(x, true)
7392
case *ScalarFunction:
7493
return pc.scalarFuncToPBExpr(x)
7594
}
@@ -190,20 +209,22 @@ func FieldTypeFromPB(ft *tipb.FieldType) *types.FieldType {
190209
return ft1
191210
}
192211

193-
func (pc PbConverter) columnToPBExpr(column *Column) *tipb.Expr {
212+
func (pc PbConverter) columnToPBExpr(column *Column, checkType bool) *tipb.Expr {
194213
if !pc.client.IsRequestTypeSupported(kv.ReqTypeSelect, int64(tipb.ExprType_ColumnRef)) {
195214
return nil
196215
}
197-
switch column.GetType().GetType() {
198-
case mysql.TypeBit:
199-
if !IsPushDownEnabled(ast.TypeStr(column.GetType().GetType()), kv.TiKV) {
200-
return nil
201-
}
202-
case mysql.TypeSet, mysql.TypeGeometry, mysql.TypeUnspecified:
203-
return nil
204-
case mysql.TypeEnum:
205-
if !IsPushDownEnabled("enum", kv.UnSpecified) {
216+
if checkType {
217+
switch column.GetType().GetType() {
218+
case mysql.TypeBit:
219+
if !IsPushDownEnabled(ast.TypeStr(mysql.TypeBit), kv.TiKV) {
220+
return nil
221+
}
222+
case mysql.TypeSet, mysql.TypeGeometry, mysql.TypeUnspecified:
206223
return nil
224+
case mysql.TypeEnum:
225+
if !IsPushDownEnabled("enum", kv.UnSpecified) {
226+
return nil
227+
}
207228
}
208229
}
209230

pkg/expression/expr_to_pb_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1913,3 +1913,20 @@ func TestPanicIfPbCodeUnspecified(t *testing.T) {
19131913
pc := PbConverter{client: new(mock.Client), ctx: mock.NewContext()}
19141914
require.PanicsWithError(t, "unspecified PbCode: *expression.builtinBitAndSig", func() { pc.ExprToPB(fn) })
19151915
}
1916+
1917+
func TestProjectionColumn2Pb(t *testing.T) {
1918+
var colExprs []Expression
1919+
ctx := mock.NewContext()
1920+
client := new(mock.Client)
1921+
1922+
colExprs = append(colExprs, genColumn(mysql.TypeSet, 1))
1923+
colExprs = append(colExprs, genColumn(mysql.TypeShort, 2))
1924+
colExprs = append(colExprs, genColumn(mysql.TypeLong, 3))
1925+
1926+
// TypeSet column can't be converted to PB by default
1927+
_, err := ExpressionsToPBList(ctx, colExprs, client)
1928+
require.Error(t, err)
1929+
1930+
_, err = ProjectionExpressionsToPBList(ctx, colExprs, client)
1931+
require.NoError(t, err)
1932+
}

pkg/expression/infer_pushdown.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,11 @@ func canExprPushDown(ctx PushDownContext, expr Expression, storeType kv.StoreTyp
140140
}
141141
switch x := expr.(type) {
142142
case *CorrelatedColumn:
143-
return pc.conOrCorColToPBExpr(expr) != nil && pc.columnToPBExpr(&x.Column) != nil
143+
return pc.conOrCorColToPBExpr(expr) != nil && pc.columnToPBExpr(&x.Column, true) != nil
144144
case *Constant:
145145
return pc.conOrCorColToPBExpr(expr) != nil
146146
case *Column:
147-
return pc.columnToPBExpr(x) != nil
147+
return pc.columnToPBExpr(x, true) != nil
148148
case *ScalarFunction:
149149
return canScalarFuncPushDown(ctx, x, storeType)
150150
}

pkg/planner/core/casetest/mpp/mpp_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,7 @@ func TestMppJoinExchangeColumnPrune(t *testing.T) {
648648
}
649649

650650
tk.MustExec("set @@tidb_allow_mpp=1;")
651+
tk.MustExec("set @@tidb_enforce_mpp=1;")
651652
tk.MustExec("set @@session.tidb_broadcast_join_threshold_size = 1")
652653
tk.MustExec("set @@session.tidb_broadcast_join_threshold_count = 1")
653654

@@ -707,6 +708,7 @@ func TestMppFineGrainedJoinAndAgg(t *testing.T) {
707708
}
708709

709710
tk.MustExec("set @@tidb_allow_mpp=1;")
711+
tk.MustExec("set @@tidb_enforce_mpp=1;")
710712
tk.MustExec("set @@session.tidb_broadcast_join_threshold_size = 1")
711713
tk.MustExec("set @@session.tidb_broadcast_join_threshold_count = 1")
712714

pkg/planner/core/optimizer.go

Lines changed: 3 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -301,10 +301,7 @@ func doOptimize(
301301
if err != nil {
302302
return nil, nil, 0, err
303303
}
304-
finalPlan, err := postOptimize(ctx, sctx, physical)
305-
if err != nil {
306-
return nil, nil, 0, err
307-
}
304+
finalPlan := postOptimize(ctx, sctx, physical)
308305

309306
if sessVars.StmtCtx.EnableOptimizerCETrace {
310307
refineCETrace(sctx)
@@ -412,13 +409,9 @@ func mergeContinuousSelections(p base.PhysicalPlan) {
412409
}
413410
}
414411

415-
func postOptimize(ctx context.Context, sctx base.PlanContext, plan base.PhysicalPlan) (base.PhysicalPlan, error) {
412+
func postOptimize(ctx context.Context, sctx base.PlanContext, plan base.PhysicalPlan) base.PhysicalPlan {
416413
// some cases from update optimize will require avoiding projection elimination.
417414
// see comments ahead of call of DoOptimize in function of buildUpdate().
418-
err := prunePhysicalColumns(sctx, plan)
419-
if err != nil {
420-
return nil, err
421-
}
422415
plan = eliminatePhysicalProjection(plan)
423416
plan = InjectExtraProjection(plan)
424417
mergeContinuousSelections(plan)
@@ -430,7 +423,7 @@ func postOptimize(ctx context.Context, sctx base.PlanContext, plan base.Physical
430423
disableReuseChunkIfNeeded(sctx, plan)
431424
tryEnableLateMaterialization(sctx, plan)
432425
generateRuntimeFilter(sctx, plan)
433-
return plan, nil
426+
return plan
434427
}
435428

436429
func generateRuntimeFilter(sctx base.PlanContext, plan base.PhysicalPlan) {
@@ -449,145 +442,6 @@ func generateRuntimeFilter(sctx base.PlanContext, plan base.PhysicalPlan) {
449442
zap.Duration("Cost", time.Since(startRFGenerator)))
450443
}
451444

452-
// prunePhysicalColumns currently only work for MPP(HashJoin<-Exchange).
453-
// Here add projection instead of pruning columns directly for safety considerations.
454-
// And projection is cheap here for it saves the network cost and work in memory.
455-
func prunePhysicalColumns(sctx base.PlanContext, plan base.PhysicalPlan) error {
456-
if tableReader, ok := plan.(*PhysicalTableReader); ok {
457-
if _, isExchangeSender := tableReader.tablePlan.(*PhysicalExchangeSender); isExchangeSender {
458-
err := prunePhysicalColumnsInternal(sctx, tableReader.tablePlan)
459-
if err != nil {
460-
return err
461-
}
462-
}
463-
} else {
464-
for _, child := range plan.Children() {
465-
return prunePhysicalColumns(sctx, child)
466-
}
467-
}
468-
return nil
469-
}
470-
471-
func (p *PhysicalHashJoin) extractUsedCols(parentUsedCols []*expression.Column) (leftCols []*expression.Column, rightCols []*expression.Column) {
472-
for _, eqCond := range p.EqualConditions {
473-
parentUsedCols = append(parentUsedCols, expression.ExtractColumns(eqCond)...)
474-
}
475-
for _, neCond := range p.NAEqualConditions {
476-
parentUsedCols = append(parentUsedCols, expression.ExtractColumns(neCond)...)
477-
}
478-
for _, leftCond := range p.LeftConditions {
479-
parentUsedCols = append(parentUsedCols, expression.ExtractColumns(leftCond)...)
480-
}
481-
for _, rightCond := range p.RightConditions {
482-
parentUsedCols = append(parentUsedCols, expression.ExtractColumns(rightCond)...)
483-
}
484-
for _, otherCond := range p.OtherConditions {
485-
parentUsedCols = append(parentUsedCols, expression.ExtractColumns(otherCond)...)
486-
}
487-
lChild := p.children[0]
488-
rChild := p.children[1]
489-
for _, col := range parentUsedCols {
490-
if lChild.Schema().Contains(col) {
491-
leftCols = append(leftCols, col)
492-
} else if rChild.Schema().Contains(col) {
493-
rightCols = append(rightCols, col)
494-
}
495-
}
496-
return leftCols, rightCols
497-
}
498-
499-
func prunePhysicalColumnForHashJoinChild(sctx base.PlanContext, hashJoin *PhysicalHashJoin, joinUsedCols []*expression.Column, sender *PhysicalExchangeSender) error {
500-
var err error
501-
evalCtx := sctx.GetExprCtx().GetEvalCtx()
502-
joinUsed := expression.GetUsedList(evalCtx, joinUsedCols, sender.Schema())
503-
hashCols := make([]*expression.Column, len(sender.HashCols))
504-
for i, mppCol := range sender.HashCols {
505-
hashCols[i] = mppCol.Col
506-
}
507-
hashUsed := expression.GetUsedList(evalCtx, hashCols, sender.Schema())
508-
509-
needPrune := false
510-
usedExprs := make([]expression.Expression, len(sender.Schema().Columns))
511-
prunedSchema := sender.Schema().Clone()
512-
for i := len(joinUsed) - 1; i >= 0; i-- {
513-
usedExprs[i] = sender.Schema().Columns[i]
514-
if !joinUsed[i] && !hashUsed[i] {
515-
needPrune = true
516-
usedExprs = append(usedExprs[:i], usedExprs[i+1:]...)
517-
prunedSchema.Columns = append(prunedSchema.Columns[:i], prunedSchema.Columns[i+1:]...)
518-
}
519-
}
520-
521-
if needPrune && len(sender.children) > 0 {
522-
ch := sender.children[0]
523-
proj := PhysicalProjection{
524-
Exprs: usedExprs,
525-
}.Init(sctx, ch.StatsInfo(), ch.QueryBlockOffset())
526-
527-
proj.SetSchema(prunedSchema)
528-
proj.SetChildren(ch)
529-
sender.children[0] = proj
530-
531-
// Resolve Indices from bottom to up
532-
err = proj.ResolveIndicesItself()
533-
if err != nil {
534-
return err
535-
}
536-
err = sender.ResolveIndicesItself()
537-
if err != nil {
538-
return err
539-
}
540-
err = hashJoin.ResolveIndicesItself()
541-
if err != nil {
542-
return err
543-
}
544-
}
545-
return err
546-
}
547-
548-
func prunePhysicalColumnsInternal(sctx base.PlanContext, plan base.PhysicalPlan) error {
549-
var err error
550-
switch x := plan.(type) {
551-
case *PhysicalHashJoin:
552-
schemaColumns := x.Schema().Clone().Columns
553-
leftCols, rightCols := x.extractUsedCols(schemaColumns)
554-
matchPattern := false
555-
for i := 0; i <= 1; i++ {
556-
// Pattern: HashJoin <- ExchangeReceiver <- ExchangeSender
557-
matchPattern = false
558-
var exchangeSender *PhysicalExchangeSender
559-
if receiver, ok := x.children[i].(*PhysicalExchangeReceiver); ok {
560-
exchangeSender, matchPattern = receiver.children[0].(*PhysicalExchangeSender)
561-
}
562-
563-
if matchPattern {
564-
if i == 0 {
565-
err = prunePhysicalColumnForHashJoinChild(sctx, x, leftCols, exchangeSender)
566-
} else {
567-
err = prunePhysicalColumnForHashJoinChild(sctx, x, rightCols, exchangeSender)
568-
}
569-
if err != nil {
570-
return nil
571-
}
572-
}
573-
574-
/// recursively travel the physical plan
575-
err = prunePhysicalColumnsInternal(sctx, x.children[i])
576-
if err != nil {
577-
return nil
578-
}
579-
}
580-
default:
581-
for _, child := range x.Children() {
582-
err = prunePhysicalColumnsInternal(sctx, child)
583-
if err != nil {
584-
return err
585-
}
586-
}
587-
}
588-
return nil
589-
}
590-
591445
// tryEnableLateMaterialization tries to push down some filter conditions to the table scan operator
592446
// @brief: push down some filter conditions to the table scan operator
593447
// @param: sctx: session context

0 commit comments

Comments
 (0)