diff --git a/pkg/planner/core/hashcode.go b/pkg/planner/core/hashcode.go index 6772f1e092fac..d6a029882265e 100644 --- a/pkg/planner/core/hashcode.go +++ b/pkg/planner/core/hashcode.go @@ -19,21 +19,16 @@ import ( "encoding/binary" "slices" + "github.com/pingcap/tidb/pkg/planner/util" "github.com/pingcap/tidb/pkg/util/plancodec" ) -func encodeIntAsUint32(result []byte, value int) []byte { - var buf [4]byte - binary.BigEndian.PutUint32(buf[:], uint32(value)) - return append(result, buf[:]...) -} - // HashCode implements LogicalPlan interface. func (p *baseLogicalPlan) HashCode() []byte { // We use PlanID for the default hash, so if two plans do not have // the same id, the hash value will never be the same. result := make([]byte, 0, 4) - result = encodeIntAsUint32(result, p.ID()) + result = util.EncodeIntAsUint32(result, p.ID()) return result } @@ -43,12 +38,12 @@ func (p *LogicalProjection) HashCode() []byte { // Expressions are commonly `Column`s, whose hashcode has the length 9, so // we pre-alloc 10 bytes for each expr's hashcode. result := make([]byte, 0, 12+len(p.Exprs)*10) - result = encodeIntAsUint32(result, plancodec.TypeStringToPhysicalID(p.TP())) - result = encodeIntAsUint32(result, p.QueryBlockOffset()) - result = encodeIntAsUint32(result, len(p.Exprs)) + result = util.EncodeIntAsUint32(result, plancodec.TypeStringToPhysicalID(p.TP())) + result = util.EncodeIntAsUint32(result, p.QueryBlockOffset()) + result = util.EncodeIntAsUint32(result, len(p.Exprs)) for _, expr := range p.Exprs { exprHashCode := expr.HashCode() - result = encodeIntAsUint32(result, len(exprHashCode)) + result = util.EncodeIntAsUint32(result, len(exprHashCode)) result = append(result, exprHashCode...) } return result @@ -58,9 +53,9 @@ func (p *LogicalProjection) HashCode() []byte { func (p *LogicalTableDual) HashCode() []byte { // PlanType + SelectOffset + RowCount result := make([]byte, 0, 12) - result = encodeIntAsUint32(result, plancodec.TypeStringToPhysicalID(p.TP())) - result = encodeIntAsUint32(result, p.QueryBlockOffset()) - result = encodeIntAsUint32(result, p.RowCount) + result = util.EncodeIntAsUint32(result, plancodec.TypeStringToPhysicalID(p.TP())) + result = util.EncodeIntAsUint32(result, p.QueryBlockOffset()) + result = util.EncodeIntAsUint32(result, p.RowCount) return result } @@ -70,9 +65,9 @@ func (p *LogicalSelection) HashCode() []byte { // Conditions are commonly `ScalarFunction`s, whose hashcode usually has a // length larger than 20, so we pre-alloc 25 bytes for each expr's hashcode. result := make([]byte, 0, 12+len(p.Conditions)*25) - result = encodeIntAsUint32(result, plancodec.TypeStringToPhysicalID(p.TP())) - result = encodeIntAsUint32(result, p.QueryBlockOffset()) - result = encodeIntAsUint32(result, len(p.Conditions)) + result = util.EncodeIntAsUint32(result, plancodec.TypeStringToPhysicalID(p.TP())) + result = util.EncodeIntAsUint32(result, p.QueryBlockOffset()) + result = util.EncodeIntAsUint32(result, len(p.Conditions)) condHashCodes := make([][]byte, len(p.Conditions)) for i, expr := range p.Conditions { @@ -82,7 +77,7 @@ func (p *LogicalSelection) HashCode() []byte { slices.SortFunc(condHashCodes, func(i, j []byte) int { return bytes.Compare(i, j) }) for _, condHashCode := range condHashCodes { - result = encodeIntAsUint32(result, len(condHashCode)) + result = util.EncodeIntAsUint32(result, len(condHashCode)) result = append(result, condHashCode...) } return result diff --git a/pkg/planner/core/plan.go b/pkg/planner/core/plan.go index ab2b60d152eac..a09be32c84cc2 100644 --- a/pkg/planner/core/plan.go +++ b/pkg/planner/core/plan.go @@ -34,6 +34,10 @@ import ( "github.com/pingcap/tidb/pkg/util/tracing" ) +func init() { + util.HasMaxOneRowUtil = HasMaxOneRow +} + // AsSctx converts PlanContext to sessionctx.Context. func AsSctx(pctx base.PlanContext) (sessionctx.Context, error) { sctx, ok := pctx.(sessionctx.Context) @@ -475,7 +479,7 @@ func (p *baseLogicalPlan) BuildKeyInfo(_ *expression.Schema, _ []*expression.Sch for i := range p.children { childMaxOneRow[i] = p.children[i].MaxOneRow() } - p.maxOneRow = HasMaxOneRow(p.self, childMaxOneRow) + p.maxOneRow = util.HasMaxOneRowUtil(p.self, childMaxOneRow) } // BuildKeyInfo implements LogicalPlan BuildKeyInfo interface. diff --git a/pkg/planner/memo/BUILD.bazel b/pkg/planner/memo/BUILD.bazel index 188c164fed8ea..3016401671b1b 100644 --- a/pkg/planner/memo/BUILD.bazel +++ b/pkg/planner/memo/BUILD.bazel @@ -16,6 +16,7 @@ go_library( "//pkg/planner/core/base", "//pkg/planner/pattern", "//pkg/planner/property", + "//pkg/planner/util", ], ) diff --git a/pkg/planner/memo/group.go b/pkg/planner/memo/group.go index 456213ac3b7ae..fd32a982d0d1d 100644 --- a/pkg/planner/memo/group.go +++ b/pkg/planner/memo/group.go @@ -19,10 +19,12 @@ import ( "fmt" "github.com/pingcap/tidb/pkg/expression" - plannercore "github.com/pingcap/tidb/pkg/planner/core" + // import core pkg first to call its init func. + _ "github.com/pingcap/tidb/pkg/planner/core" "github.com/pingcap/tidb/pkg/planner/core/base" "github.com/pingcap/tidb/pkg/planner/pattern" "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" ) // ExploreMark is uses to mark whether a Group or GroupExpr has @@ -221,5 +223,5 @@ func (g *Group) BuildKeyInfo() { g.Prop.Schema.Keys = childSchema[0].Keys } e.ExprNode.BuildKeyInfo(g.Prop.Schema, childSchema) - g.Prop.MaxOneRow = e.ExprNode.MaxOneRow() || plannercore.HasMaxOneRow(e.ExprNode, childMaxOneRow) + g.Prop.MaxOneRow = e.ExprNode.MaxOneRow() || util.HasMaxOneRowUtil(e.ExprNode, childMaxOneRow) } diff --git a/pkg/planner/util/BUILD.bazel b/pkg/planner/util/BUILD.bazel index 50fa82be4a09a..26b5ec834e054 100644 --- a/pkg/planner/util/BUILD.bazel +++ b/pkg/planner/util/BUILD.bazel @@ -5,6 +5,7 @@ go_library( srcs = [ "byitem.go", "expression.go", + "func_pointer_misc.go", "handle_cols.go", "misc.go", "null_misc.go", @@ -19,6 +20,7 @@ go_library( "//pkg/parser/model", "//pkg/parser/mysql", "//pkg/planner/context", + "//pkg/planner/core/base", "//pkg/sessionctx/stmtctx", "//pkg/tablecodec", "//pkg/types", diff --git a/pkg/planner/util/func_pointer_misc.go b/pkg/planner/util/func_pointer_misc.go new file mode 100644 index 0000000000000..b13198959669c --- /dev/null +++ b/pkg/planner/util/func_pointer_misc.go @@ -0,0 +1,25 @@ +// 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 util + +import "github.com/pingcap/tidb/pkg/planner/core/base" + +// this file is used for passing function pointer at init(){} to avoid some import cycles. + +// HasMaxOneRowUtil is used in baseLogicalPlan implementation of LogicalPlan interface, while +// the original HasMaxOneRowUtil has some dependency of original core pkg: like Datasource which +// hasn't been moved out of core pkg, so associative func pointer is introduced. +// todo: (1) arenatlx, remove this func pointer when concrete Logical Operators moved out. +var HasMaxOneRowUtil func(p base.LogicalPlan, childMaxOneRow []bool) bool diff --git a/pkg/planner/util/misc.go b/pkg/planner/util/misc.go index 8ec3612687e7c..3589a764ff694 100644 --- a/pkg/planner/util/misc.go +++ b/pkg/planner/util/misc.go @@ -15,6 +15,7 @@ package util import ( + "encoding/binary" "fmt" "time" "unsafe" @@ -88,3 +89,10 @@ func (tr *QueryTimeRange) MemoryUsage() (sum int64) { } return emptyQueryTimeRangeSize } + +// EncodeIntAsUint32 is used for LogicalPlan Interface +func EncodeIntAsUint32(result []byte, value int) []byte { + var buf [4]byte + binary.BigEndian.PutUint32(buf[:], uint32(value)) + return append(result, buf[:]...) +}