planner: fix max-one-row will hash equals to each other because of no elements inside. (#57797)
ref pingcap/tidb#51664
This commit is contained in:
@ -39,8 +39,8 @@ func GenHash64Equals4LogicalOps() ([]byte, error) {
|
||||
var structures = []any{logicalop.LogicalJoin{}, logicalop.LogicalAggregation{}, logicalop.LogicalApply{},
|
||||
logicalop.LogicalExpand{}, logicalop.LogicalLimit{}, logicalop.LogicalMaxOneRow{}, logicalop.DataSource{},
|
||||
logicalop.LogicalMemTable{}, logicalop.LogicalUnionAll{}, logicalop.LogicalPartitionUnionAll{}, logicalop.LogicalProjection{},
|
||||
logicalop.LogicalSelection{}, logicalop.LogicalShow{}, logicalop.LogicalShowDDLJobs{}, logicalop.LogicalSort{},
|
||||
logicalop.LogicalTableDual{}, logicalop.LogicalTopN{}, logicalop.LogicalUnionScan{}, logicalop.LogicalWindow{},
|
||||
logicalop.LogicalSelection{}, logicalop.LogicalSequence{}, logicalop.LogicalShow{}, logicalop.LogicalShowDDLJobs{},
|
||||
logicalop.LogicalSort{}, logicalop.LogicalTableDual{}, logicalop.LogicalTopN{}, logicalop.LogicalUnionScan{}, logicalop.LogicalWindow{},
|
||||
}
|
||||
c := new(cc)
|
||||
c.write(codeGenHash64EqualsPrefix)
|
||||
@ -139,6 +139,8 @@ func logicalOpName2PlanCodecString(name string) string {
|
||||
return "plancodec.TypeProj"
|
||||
case "LogicalSelection":
|
||||
return "plancodec.TypeSel"
|
||||
case "LogicalSequence":
|
||||
return "plancodec.TypeSequence"
|
||||
case "LogicalShow":
|
||||
return "plancodec.TypeShow"
|
||||
case "LogicalShowDDLJobs":
|
||||
|
||||
@ -56,13 +56,21 @@ type BaseLogicalPlan struct {
|
||||
|
||||
// Hash64 implements HashEquals.<0th> interface.
|
||||
func (p *BaseLogicalPlan) Hash64(h base2.Hasher) {
|
||||
intest.Assert(false, "Hash64 should not be called directly")
|
||||
_, ok1 := p.self.(*LogicalSequence)
|
||||
_, ok2 := p.self.(*LogicalMaxOneRow)
|
||||
if !ok1 && !ok2 {
|
||||
intest.Assert(false, "Hash64 should not be called directly")
|
||||
}
|
||||
h.HashInt(p.ID())
|
||||
}
|
||||
|
||||
// Equals implements HashEquals.<1st> interface.
|
||||
func (p *BaseLogicalPlan) Equals(other any) bool {
|
||||
intest.Assert(false, "Equals should not be called directly")
|
||||
_, ok1 := p.self.(*LogicalSequence)
|
||||
_, ok2 := p.self.(*LogicalMaxOneRow)
|
||||
if !ok1 && !ok2 {
|
||||
intest.Assert(false, "Equals should not be called directly")
|
||||
}
|
||||
if other == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -452,6 +452,7 @@ func (op *LogicalLimit) Equals(other any) bool {
|
||||
// Hash64 implements the Hash64Equals interface.
|
||||
func (op *LogicalMaxOneRow) Hash64(h base.Hasher) {
|
||||
h.HashString(plancodec.TypeMaxOneRow)
|
||||
op.BaseLogicalPlan.Hash64(h)
|
||||
}
|
||||
|
||||
// Equals implements the Hash64Equals interface, only receive *LogicalMaxOneRow pointer.
|
||||
@ -466,7 +467,9 @@ func (op *LogicalMaxOneRow) Equals(other any) bool {
|
||||
if op2 == nil {
|
||||
return false
|
||||
}
|
||||
_ = op2
|
||||
if !op.BaseLogicalPlan.Equals(&op2.BaseLogicalPlan) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -725,6 +728,30 @@ func (op *LogicalSelection) Equals(other any) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Hash64 implements the Hash64Equals interface.
|
||||
func (op *LogicalSequence) Hash64(h base.Hasher) {
|
||||
h.HashString(plancodec.TypeSequence)
|
||||
op.BaseLogicalPlan.Hash64(h)
|
||||
}
|
||||
|
||||
// Equals implements the Hash64Equals interface, only receive *LogicalSequence pointer.
|
||||
func (op *LogicalSequence) Equals(other any) bool {
|
||||
op2, ok := other.(*LogicalSequence)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if op == nil {
|
||||
return op2 == nil
|
||||
}
|
||||
if op2 == nil {
|
||||
return false
|
||||
}
|
||||
if !op.BaseLogicalPlan.Equals(&op2.BaseLogicalPlan) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Hash64 implements the Hash64Equals interface.
|
||||
func (op *LogicalShow) Hash64(h base.Hasher) {
|
||||
h.HashString(plancodec.TypeShow)
|
||||
|
||||
@ -26,7 +26,8 @@ import (
|
||||
|
||||
// LogicalMaxOneRow checks if a query returns no more than one row.
|
||||
type LogicalMaxOneRow struct {
|
||||
BaseLogicalPlan
|
||||
// logical max one row, doesn't have any other attribute to distinguish, use plan id inside.
|
||||
BaseLogicalPlan `hash64-equals:"true"`
|
||||
}
|
||||
|
||||
// Init initializes LogicalMaxOneRow.
|
||||
|
||||
@ -34,7 +34,8 @@ import (
|
||||
//
|
||||
// We use this property to do complex optimizations for CTEs.
|
||||
type LogicalSequence struct {
|
||||
BaseLogicalPlan
|
||||
// logical sequence doesn't have any other attribute to distinguish, use plan id inside.
|
||||
BaseLogicalPlan `hash64-equals:"true"`
|
||||
}
|
||||
|
||||
// Init initializes LogicalSequence
|
||||
|
||||
@ -8,7 +8,7 @@ go_test(
|
||||
"logical_mem_table_predicate_extractor_test.go",
|
||||
],
|
||||
flaky = True,
|
||||
shard_count = 27,
|
||||
shard_count = 28,
|
||||
deps = [
|
||||
"//pkg/domain",
|
||||
"//pkg/expression",
|
||||
|
||||
@ -33,6 +33,26 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLogicalSequence(t *testing.T) {
|
||||
ctx := mock.NewContext()
|
||||
m1 := logicalop.LogicalSequence{}.Init(ctx, 1)
|
||||
m2 := logicalop.LogicalSequence{}.Init(ctx, 1)
|
||||
// since logical max one row doesn't have any elements, they are always indicate
|
||||
// that they are equal.
|
||||
hasher1 := base.NewHashEqualer()
|
||||
hasher2 := base.NewHashEqualer()
|
||||
m1.Hash64(hasher1)
|
||||
m2.Hash64(hasher2)
|
||||
require.NotEqual(t, hasher1.Sum64(), hasher2.Sum64())
|
||||
require.False(t, m1.Equals(m2))
|
||||
|
||||
m2.SetID(m1.ID())
|
||||
hasher2.Reset()
|
||||
m2.Hash64(hasher2)
|
||||
require.Equal(t, hasher1.Sum64(), hasher2.Sum64())
|
||||
require.True(t, m1.Equals(m2))
|
||||
}
|
||||
|
||||
func TestLogicalSelectionHash64Equals(t *testing.T) {
|
||||
col1 := &expression.Column{
|
||||
ID: 1,
|
||||
@ -278,14 +298,21 @@ func TestLogicalSchemaProducerHash64Equals(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLogicalMaxOneRowHash64Equals(t *testing.T) {
|
||||
m1 := &logicalop.LogicalMaxOneRow{}
|
||||
m2 := &logicalop.LogicalMaxOneRow{}
|
||||
ctx := mock.NewContext()
|
||||
m1 := logicalop.LogicalMaxOneRow{}.Init(ctx, 1)
|
||||
m2 := logicalop.LogicalMaxOneRow{}.Init(ctx, 1)
|
||||
// since logical max one row doesn't have any elements, they are always indicate
|
||||
// that they are equal.
|
||||
hasher1 := base.NewHashEqualer()
|
||||
hasher2 := base.NewHashEqualer()
|
||||
m1.Hash64(hasher1)
|
||||
m2.Hash64(hasher2)
|
||||
require.NotEqual(t, hasher1.Sum64(), hasher2.Sum64())
|
||||
require.False(t, m1.Equals(m2))
|
||||
|
||||
m2.SetID(m1.ID())
|
||||
hasher2.Reset()
|
||||
m2.Hash64(hasher2)
|
||||
require.Equal(t, hasher1.Sum64(), hasher2.Sum64())
|
||||
require.True(t, m1.Equals(m2))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user