// Copyright 2019 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // See the License for the specific language governing permissions and // limitations under the License. package core import ( "bytes" "encoding/binary" "sort" "github.com/pingcap/tidb/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) return result } // HashCode implements LogicalPlan interface. func (p *LogicalProjection) HashCode() []byte { // PlanType + SelectOffset + ExprNum + [Exprs] // 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.SelectBlockOffset()) result = encodeIntAsUint32(result, len(p.Exprs)) for _, expr := range p.Exprs { exprHashCode := expr.HashCode(p.ctx.GetSessionVars().StmtCtx) result = encodeIntAsUint32(result, len(exprHashCode)) result = append(result, exprHashCode...) } return result } // HashCode implements LogicalPlan interface. func (p *LogicalTableDual) HashCode() []byte { // PlanType + SelectOffset + RowCount result := make([]byte, 0, 12) result = encodeIntAsUint32(result, plancodec.TypeStringToPhysicalID(p.tp)) result = encodeIntAsUint32(result, p.SelectBlockOffset()) result = encodeIntAsUint32(result, p.RowCount) return result } // HashCode implements LogicalPlan interface. func (p *LogicalSelection) HashCode() []byte { // PlanType + SelectOffset + ConditionNum + [Conditions] // 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.SelectBlockOffset()) result = encodeIntAsUint32(result, len(p.Conditions)) condHashCodes := make([][]byte, len(p.Conditions)) for i, expr := range p.Conditions { condHashCodes[i] = expr.HashCode(p.ctx.GetSessionVars().StmtCtx) } // Sort the conditions, so `a > 1 and a < 100` can equal to `a < 100 and a > 1`. sort.Slice(condHashCodes, func(i, j int) bool { return bytes.Compare(condHashCodes[i], condHashCodes[j]) < 0 }) for _, condHashCode := range condHashCodes { result = encodeIntAsUint32(result, len(condHashCode)) result = append(result, condHashCode...) } return result } // HashCode implements LogicalPlan interface. func (p *LogicalLimit) HashCode() []byte { // PlanType + SelectOffset + Offset + Count result := make([]byte, 24) binary.BigEndian.PutUint32(result, uint32(plancodec.TypeStringToPhysicalID(p.tp))) binary.BigEndian.PutUint32(result[4:], uint32(p.SelectBlockOffset())) binary.BigEndian.PutUint64(result[8:], p.Offset) binary.BigEndian.PutUint64(result[16:], p.Count) return result }