Skip to content

Commit

Permalink
planner: introduce hashEquals for expression.Column/collationInfo/fie…
Browse files Browse the repository at this point in the history
…ldType (#55691)

ref #51664
  • Loading branch information
AilinKid authored Sep 2, 2024
1 parent dd64d25 commit dd114f1
Show file tree
Hide file tree
Showing 7 changed files with 466 additions and 1 deletion.
3 changes: 3 additions & 0 deletions pkg/expression/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ go_library(
"//pkg/parser/opcode",
"//pkg/parser/terror",
"//pkg/parser/types",
"//pkg/planner/cascades/base",
"//pkg/sessionctx/stmtctx",
"//pkg/sessionctx/variable",
"//pkg/types",
Expand Down Expand Up @@ -212,6 +213,7 @@ go_test(
"//pkg/parser/model",
"//pkg/parser/mysql",
"//pkg/parser/terror",
"//pkg/planner/cascades/base",
"//pkg/planner/core",
"//pkg/planner/core/resolve",
"//pkg/session",
Expand Down Expand Up @@ -246,6 +248,7 @@ go_test(
"@com_github_tikv_client_go_v2//oracle",
"@com_github_tikv_client_go_v2//tikv",
"@io_opencensus_go//stats/view",
"@org_uber_go_atomic//:atomic",
"@org_uber_go_goleak//:goleak",
],
)
34 changes: 34 additions & 0 deletions pkg/expression/collation.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/pingcap/tidb/pkg/parser/ast"
"github.com/pingcap/tidb/pkg/parser/charset"
"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/collate"
Expand All @@ -28,6 +29,8 @@ import (
"go.uber.org/atomic"
)

var _ base.HashEquals = &collationInfo{}

// ExprCollation is a struct that store the collation related information.
type ExprCollation struct {
Coer Coercibility
Expand All @@ -45,6 +48,37 @@ type collationInfo struct {
collation string
}

// Hash64 implements the base.Hasher.<0th> interface.
func (c *collationInfo) Hash64(h base.Hasher) {
h.HashInt64(int64(c.coer))
h.HashBool(c.coerInit.Load())
h.HashInt(int(c.repertoire))
h.HashString(c.charset)
h.HashString(c.collation)
}

// Equals implements the base.Hasher.<1th> interface.
func (c *collationInfo) Equals(other any) bool {
// the caller should care about c is nil or not.
if other == nil {
return false
}
var c2 *collationInfo
switch x := other.(type) {
case *collationInfo:
c2 = x
case collationInfo:
c2 = &x
default:
return false
}
return c.coer == c2.coer &&
c.coerInit.Load() == c2.coerInit.Load() &&
c.repertoire == c2.repertoire &&
c.charset == c2.charset &&
c.collation == c2.collation
}

func (c *collationInfo) HasCoercibility() bool {
return c.coerInit.Load()
}
Expand Down
67 changes: 67 additions & 0 deletions pkg/expression/collation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ import (
"github.com/pingcap/tidb/pkg/parser/ast"
"github.com/pingcap/tidb/pkg/parser/charset"
"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/mock"
"github.com/stretchr/testify/require"
"go.uber.org/atomic"
)

func newExpression(coercibility Coercibility, repertoire Repertoire, chs, coll string) Expression {
Expand All @@ -33,6 +35,71 @@ func newExpression(coercibility Coercibility, repertoire Repertoire, chs, coll s
return constant
}

func TestCollationHashEquals(t *testing.T) {
c1 := collationInfo{
coer: 1,
coerInit: atomic.Bool{},
repertoire: 1,
charset: "aa",
collation: "bb",
}
c2 := collationInfo{
coer: 1,
coerInit: atomic.Bool{},
repertoire: 1,
charset: "aabb",
collation: "",
}
hasher1 := base.NewHashEqualer()
hasher2 := base.NewHashEqualer()
c1.Hash64(hasher1)
c2.Hash64(hasher2)
require.NotEqual(t, hasher1.Sum64(), hasher2.Sum64())
require.False(t, c1.Equals(c2))

c2.charset = "aa"
c2.collation = "bb"
hasher2.Reset()
c2.Hash64(hasher2)
require.Equal(t, hasher1.Sum64(), hasher2.Sum64())
require.True(t, c1.Equals(c2))

c2.coer = 2
hasher2.Reset()
c2.Hash64(hasher2)
require.NotEqual(t, hasher1.Sum64(), hasher2.Sum64())
require.False(t, c1.Equals(c2))

c2.coer = 1
c2.coerInit.Store(true)
hasher2.Reset()
c2.Hash64(hasher2)
require.NotEqual(t, hasher1.Sum64(), hasher2.Sum64())
require.False(t, c1.Equals(c2))

c2.coerInit.Store(false)
c2.repertoire = 2
hasher2.Reset()
c2.Hash64(hasher2)
require.NotEqual(t, hasher1.Sum64(), hasher2.Sum64())
require.False(t, c1.Equals(c2))

c2.repertoire = 1
c2.charset = ""
c2.collation = "aabb"
hasher2.Reset()
c2.Hash64(hasher2)
require.NotEqual(t, hasher1.Sum64(), hasher2.Sum64())
require.False(t, c1.Equals(c2))

c2.charset = "aa"
c2.collation = "bb"
hasher2.Reset()
c2.Hash64(hasher2)
require.Equal(t, hasher1.Sum64(), hasher2.Sum64())
require.True(t, c1.Equals(c2))
}

func TestInferCollation(t *testing.T) {
tests := []struct {
exprs []Expression
Expand Down
60 changes: 60 additions & 0 deletions pkg/expression/column.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,17 @@ import (
"github.com/pingcap/tidb/pkg/parser/charset"
"github.com/pingcap/tidb/pkg/parser/model"
"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"
"github.com/pingcap/tidb/pkg/util/size"
)

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

// CorrelatedColumn stands for a column in a correlated sub query.
type CorrelatedColumn struct {
Column
Expand Down Expand Up @@ -442,6 +447,61 @@ func (col *Column) GetStaticType() *types.FieldType {
return col.RetType
}

// Hash64 implements HashEquals.<0th> interface.
func (col *Column) Hash64(h base.Hasher) {
if col.RetType == nil {
h.HashByte(base.NilFlag)
} else {
h.HashByte(base.NotNilFlag)
col.RetType.Hash64(h)
}
h.HashInt64(col.ID)
h.HashInt64(col.UniqueID)
h.HashInt(col.Index)
if col.VirtualExpr != nil {
h.HashByte(base.NilFlag)
} else {
h.HashByte(base.NotNilFlag)
//col.VirtualExpr.Hash64(h)
}
h.HashString(col.OrigName)
h.HashBool(col.IsHidden)
h.HashBool(col.IsPrefix)
h.HashBool(col.InOperand)
col.collationInfo.Hash64(h)
h.HashInt64(col.CorrelatedColUniqueID)
}

// Equals implements HashEquals.<1st> interface.
func (col *Column) Equals(other any) bool {
if other == nil {
return false
}
var col2 *Column
switch x := other.(type) {
case Column:
col2 = &x
case *Column:
col2 = x
default:
return false
}
// when step into here, we could ensure that col1.RetType and col2.RetType are same type.
// and we should ensure col1.RetType and col2.RetType is not nil ourselves.
ftEqual := col.RetType == nil && col2.RetType == nil || col.RetType != nil && col2.RetType != nil && col.RetType.Equal(col2.RetType)
return ftEqual &&
col.ID == col2.ID &&
col.UniqueID == col2.UniqueID &&
col.Index == col2.Index &&
//col.VirtualExpr.Equals(col2.VirtualExpr) &&
col.OrigName == col2.OrigName &&
col.IsHidden == col2.IsHidden &&
col.IsPrefix == col2.IsPrefix &&
col.InOperand == col2.InOperand &&
col.collationInfo.Equals(&col2.collationInfo) &&
col.CorrelatedColUniqueID == col2.CorrelatedColUniqueID
}

// Traverse implements the TraverseDown interface.
func (col *Column) Traverse(action TraverseAction) Expression {
return action.Transform(col)
Expand Down
Loading

0 comments on commit dd114f1

Please sign in to comment.