Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

planner: introduce hashEquals interface for expression.Expression #55793

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
.
Signed-off-by: arenatlx <314806019@qq.com>
  • Loading branch information
AilinKid committed Sep 2, 2024
commit 6e6fad56eec5ac43779c15967712e9fdef4d3910
26 changes: 26 additions & 0 deletions pkg/expression/column.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (

var (
_ base.HashEquals = &Column{}
_ base.HashEquals = &CorrelatedColumn{}
)

// CorrelatedColumn stands for a column in a correlated sub query.
Expand Down Expand Up @@ -246,6 +247,31 @@ func (col *CorrelatedColumn) RemapColumn(m map[int64]*Column) (Expression, error
}, nil
}

// Hash64 implements HashEquals.<0th> interface.
func (col *CorrelatedColumn) Hash64(h base.Hasher) {
// correlatedColumn flag here is used to distinguish correlatedColumn and Column.
h.HashByte(correlatedColumn)
col.Column.Hash64(h)
// since col.Datum is filled in the runtime, we can't use it to calculate hash now, correlatedColumn flag + column is enough.
}

// Equals implements HashEquals.<1st> interface.
func (col *CorrelatedColumn) Equals(other any) bool {
if other == nil {
return false
}
var col2 *CorrelatedColumn
switch x := other.(type) {
case CorrelatedColumn:
col2 = &x
case *CorrelatedColumn:
col2 = x
default:
return false
}
return col.Column.Equals(&col2.Column)
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't need to check Data in CorrelatedColumn?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

runtime bound data shouldn‘t be cared in planner phase

Copy link
Contributor

Choose a reason for hiding this comment

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

OK

}

// Column represents a column.
type Column struct {
RetType *types.FieldType `plan-cache-clone:"shallow"`
Expand Down
47 changes: 47 additions & 0 deletions pkg/expression/constant.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

perrors "github.com/pingcap/errors"
"github.com/pingcap/tidb/pkg/parser/mysql"
"github.com/pingcap/tidb/pkg/planner/cascades/base"
"github.com/pingcap/tidb/pkg/types"
"github.com/pingcap/tidb/pkg/util/chunk"
"github.com/pingcap/tidb/pkg/util/codec"
Expand All @@ -29,6 +30,8 @@ import (
"go.uber.org/zap"
)

var _ base.HashEquals = &Constant{}

// NewOne stands for a number 1.
func NewOne() *Constant {
retT := types.NewFieldType(mysql.TypeTiny)
Expand Down Expand Up @@ -502,6 +505,50 @@ func (c *Constant) CanonicalHashCode() []byte {
return c.getHashCode(true)
}

// Hash64 implements HashEquals.<0th> interface.
func (c *Constant) Hash64(h base.Hasher) {
if c.RetType == nil {
h.HashByte(base.NilFlag)
} else {
h.HashByte(base.NotNilFlag)
c.RetType.Hash64(h)
}
c.collationInfo.Hash64(h)
if c.DeferredExpr != nil {
c.DeferredExpr.Hash64(h)
return
}
if c.ParamMarker != nil {
h.HashByte(parameterFlag)
h.HashInt64(int64(c.ParamMarker.order))
return
}
intest.Assert(c.DeferredExpr == nil && c.ParamMarker == nil)
h.HashByte(constantFlag)
c.Value.Hash64(h)
}

// Equals implements HashEquals.<1st> interface.
func (c *Constant) Equals(other any) bool {
if other == nil {
return false
}
var c2 *Constant
switch x := other.(type) {
case *Constant:
c2 = x
case Constant:
c2 = &x
default:
return false
}
ok := c.RetType == nil && c2.RetType == nil || c.RetType != nil && c2.RetType != nil && c.RetType.Equals(c2.RetType)
ok = ok && c.collationInfo.Equals(c2.collationInfo)
ok = ok && (c.DeferredExpr == nil && c2.DeferredExpr == nil || c.DeferredExpr != nil && c2.DeferredExpr != nil && c.DeferredExpr.Equals(c2.DeferredExpr))
ok = ok && (c.ParamMarker == nil && c2.ParamMarker == nil || c.ParamMarker != nil && c2.ParamMarker != nil && c.ParamMarker.order == c2.ParamMarker.order)
return ok && c.Value.Equals(c2.Value)
}

func (c *Constant) getHashCode(canonical bool) []byte {
if len(c.hashcode) > 0 {
return c.hashcode
Expand Down
3 changes: 3 additions & 0 deletions pkg/expression/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/pingcap/tidb/pkg/parser/mysql"
"github.com/pingcap/tidb/pkg/parser/opcode"
"github.com/pingcap/tidb/pkg/parser/terror"
"github.com/pingcap/tidb/pkg/planner/cascades/base"
"github.com/pingcap/tidb/pkg/types"
"github.com/pingcap/tidb/pkg/util/chunk"
"github.com/pingcap/tidb/pkg/util/generatedexpr"
Expand All @@ -41,6 +42,7 @@ const (
scalarFunctionFlag byte = 3
parameterFlag byte = 4
ScalarSubQFlag byte = 5
correlatedColumn byte = 6
)

// EvalSimpleAst evaluates a simple ast expression directly.
Expand Down Expand Up @@ -169,6 +171,7 @@ const (
type Expression interface {
VecExpr
CollationInfo
base.HashEquals

Traverse(TraverseAction) Expression

Expand Down
43 changes: 43 additions & 0 deletions pkg/expression/scalar_function.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/pingcap/tidb/pkg/parser/model"
"github.com/pingcap/tidb/pkg/parser/mysql"
"github.com/pingcap/tidb/pkg/parser/terror"
"github.com/pingcap/tidb/pkg/planner/cascades/base"
"github.com/pingcap/tidb/pkg/sessionctx/variable"
"github.com/pingcap/tidb/pkg/types"
"github.com/pingcap/tidb/pkg/util/chunk"
Expand All @@ -35,6 +36,8 @@ import (
"github.com/pingcap/tidb/pkg/util/intest"
)

var _ base.HashEquals = &ScalarFunction{}

// ScalarFunction is the function that returns a value.
type ScalarFunction struct {
FuncName model.CIStr
Expand Down Expand Up @@ -673,6 +676,46 @@ func simpleCanonicalizedHashCode(sf *ScalarFunction) {
}
}

// Hash64 implements HashEquals.<0th> interface.
func (sf *ScalarFunction) Hash64(h base.Hasher) {
h.HashByte(scalarFunctionFlag)
h.HashString(sf.FuncName.L)
if sf.RetType != nil {
h.HashByte(base.NilFlag)
} else {
h.HashByte(base.NotNilFlag)
sf.RetType.Hash64(h)
}
for _, arg := range sf.GetArgs() {
arg.Hash64(h)
}
}

// Equals implements HashEquals.<1th> interface.
func (sf *ScalarFunction) Equals(other any) bool {
if other == nil {
return false
}
var sf2 *ScalarFunction
switch x := other.(type) {
case *ScalarFunction:
sf2 = x
case ScalarFunction:
sf2 = &x
default:
return false
}
ok := sf.FuncName.L == sf2.FuncName.L
ok = ok && (sf.RetType == nil && sf2.RetType == nil || sf.RetType != nil && sf2.RetType != nil && sf.RetType.Equals(sf2.RetType))
for _, arg := range sf.GetArgs() {
ok = ok && arg.Equals(arg)
if !ok {
return false
}
}
return ok
}

// ReHashCode is used after we change the argument in place.
func ReHashCode(sf *ScalarFunction) {
sf.hashcode = sf.hashcode[:0]
Expand Down
3 changes: 3 additions & 0 deletions pkg/expression/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package expression

import (
"context"
"github.com/pingcap/tidb/pkg/planner/cascades/base"
"testing"
"time"

Expand Down Expand Up @@ -661,3 +662,5 @@ func (m *MockExpr) MemoryUsage() (sum int64) {
func (m *MockExpr) Traverse(action TraverseAction) Expression {
return action.Transform(m)
}
func (m *MockExpr) Hash64(_ base.Hasher) {}
func (m *MockExpr) Equals(_ any) bool { return false }
1 change: 1 addition & 0 deletions pkg/parser/types/field_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ func (ft *FieldType) Hash64(h IHasher) {
}

// Equals implements the cascades/base.Hasher.<1th> interface.
// Equals is different from Equal, it rawly compares the basic elements of two tps
func (ft *FieldType) Equals(other any) bool {
if other == nil {
return false
Expand Down
24 changes: 24 additions & 0 deletions pkg/planner/core/scalar_subq_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/pingcap/errors"
"github.com/pingcap/tidb/pkg/expression"
"github.com/pingcap/tidb/pkg/infoschema"
base2 "github.com/pingcap/tidb/pkg/planner/cascades/base"
"github.com/pingcap/tidb/pkg/planner/core/base"
"github.com/pingcap/tidb/pkg/planner/core/operator/baseimpl"
"github.com/pingcap/tidb/pkg/types"
Expand Down Expand Up @@ -224,6 +225,29 @@ func (s *ScalarSubQueryExpr) ExplainNormalizedInfo() string {
return s.String()
}

// Hash64 implements the HashEquals.<0th> interface.
func (s *ScalarSubQueryExpr) Hash64(h base2.Hasher) {
h.HashByte(expression.ScalarSubQFlag)
h.HashInt64(s.scalarSubqueryColID)
}

// Equals implements the HashEquals.<1st> interface.
func (s *ScalarSubQueryExpr) Equals(other any) bool {
if other == nil {
return false
}
var s2 *ScalarSubQueryExpr
switch x := other.(type) {
case *ScalarSubQueryExpr:
s2 = x
case ScalarSubQueryExpr:
s2 = &x
default:
return false
}
return s.scalarSubqueryColID == s2.scalarSubqueryColID
}

// HashCode implements the Expression interface.
func (s *ScalarSubQueryExpr) HashCode() []byte {
if len(s.hashcode) != 0 {
Expand Down