Skip to content

Commit 9929479

Browse files
authored
fix panic in stats histogram union and intersect (#3116)
1 parent 53a2edb commit 9929479

File tree

2 files changed

+140
-20
lines changed

2 files changed

+140
-20
lines changed

sql/stats/filter.go

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,21 @@ func Union(ctx *sql.Context, b1, b2 []sql.HistogramBucket, types []sql.Type) ([]
3232
if err != nil {
3333
return nil, err
3434
}
35-
switch cmp {
36-
case 0:
37-
if k == len(key1)-1 {
38-
ret = append(ret, b1[i])
39-
i++
40-
j++
41-
}
42-
continue
43-
case +1:
35+
if cmp == +1 {
4436
ret = append(ret, b2[j])
4537
j++
46-
case -1:
38+
break
39+
}
40+
if cmp == -1 {
4741
ret = append(ret, b1[i])
4842
i++
43+
break
44+
}
45+
// if keys are equal, merge buckets
46+
if k == len(key1)-1 {
47+
ret = append(ret, b1[i])
48+
i++
49+
j++
4950
}
5051
}
5152
}
@@ -78,18 +79,19 @@ func Intersect(ctx *sql.Context, b1, b2 []sql.HistogramBucket, types []sql.Type)
7879
if err != nil {
7980
return nil, err
8081
}
81-
switch cmp {
82-
case 0:
83-
if k == len(key1)-1 {
84-
ret = append(ret, b1[i])
85-
i++
86-
j++
87-
}
88-
continue
89-
case +1:
82+
if cmp == +1 {
9083
j++
91-
case -1:
84+
break
85+
}
86+
if cmp == -1 {
9287
i++
88+
break
89+
}
90+
// if keys are equal, merge buckets
91+
if k == len(key1)-1 {
92+
ret = append(ret, b1[i])
93+
i++
94+
j++
9395
}
9496
}
9597
}

sql/stats/filter_test.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,3 +962,121 @@ func TestUpdateCounts(t *testing.T) {
962962
})
963963
}
964964
}
965+
966+
func TestHistogramUnion(t *testing.T) {
967+
ctx := sql.NewEmptyContext()
968+
tests := []struct {
969+
types []sql.Type
970+
h1, h2 []sql.HistogramBucket
971+
exp []sql.HistogramBucket
972+
}{
973+
{
974+
types: []sql.Type{types.Int64},
975+
h1: []sql.HistogramBucket{
976+
&Bucket{BoundVal: []interface{}{9}},
977+
},
978+
h2: []sql.HistogramBucket{
979+
&Bucket{BoundVal: []interface{}{1}},
980+
},
981+
exp: []sql.HistogramBucket{
982+
&Bucket{BoundVal: []interface{}{1}},
983+
&Bucket{BoundVal: []interface{}{9}},
984+
},
985+
},
986+
{
987+
types: []sql.Type{types.Int64, types.Int64},
988+
h1: []sql.HistogramBucket{
989+
&Bucket{BoundVal: []interface{}{1, 1}},
990+
&Bucket{BoundVal: []interface{}{1, 9}},
991+
},
992+
h2: []sql.HistogramBucket{
993+
&Bucket{BoundVal: []interface{}{9, 9}},
994+
},
995+
exp: []sql.HistogramBucket{
996+
&Bucket{BoundVal: []interface{}{1, 1}},
997+
&Bucket{BoundVal: []interface{}{1, 9}},
998+
&Bucket{BoundVal: []interface{}{9, 9}},
999+
},
1000+
},
1001+
{
1002+
types: []sql.Type{types.Int64, types.Int64},
1003+
h1: []sql.HistogramBucket{
1004+
&Bucket{BoundVal: []interface{}{1, 9}},
1005+
&Bucket{BoundVal: []interface{}{9, 9}},
1006+
},
1007+
h2: []sql.HistogramBucket{
1008+
&Bucket{BoundVal: []interface{}{1, 1}},
1009+
},
1010+
exp: []sql.HistogramBucket{
1011+
&Bucket{BoundVal: []interface{}{1, 1}},
1012+
&Bucket{BoundVal: []interface{}{1, 9}},
1013+
&Bucket{BoundVal: []interface{}{9, 9}},
1014+
},
1015+
},
1016+
}
1017+
for _, tt := range tests {
1018+
t.Run(fmt.Sprintf("union: %v and %v", tt.h1, tt.h2), func(t *testing.T) {
1019+
res, err := Union(ctx, tt.h1, tt.h2, tt.types)
1020+
require.NoError(t, err)
1021+
require.Equal(t, tt.exp, res)
1022+
})
1023+
}
1024+
}
1025+
1026+
func TestHistogramIntersect(t *testing.T) {
1027+
ctx := sql.NewEmptyContext()
1028+
tests := []struct {
1029+
types []sql.Type
1030+
h1, h2 []sql.HistogramBucket
1031+
exp []sql.HistogramBucket
1032+
}{
1033+
{
1034+
types: []sql.Type{types.Int64},
1035+
h1: []sql.HistogramBucket{
1036+
&Bucket{BoundVal: []interface{}{1}},
1037+
},
1038+
h2: []sql.HistogramBucket{
1039+
&Bucket{BoundVal: []interface{}{1}},
1040+
},
1041+
exp: []sql.HistogramBucket{
1042+
&Bucket{BoundVal: []interface{}{1}},
1043+
},
1044+
},
1045+
{
1046+
types: []sql.Type{types.Int64, types.Int64},
1047+
h1: []sql.HistogramBucket{
1048+
&Bucket{BoundVal: []interface{}{1, 1}},
1049+
&Bucket{BoundVal: []interface{}{1, 9}},
1050+
},
1051+
h2: []sql.HistogramBucket{
1052+
&Bucket{BoundVal: []interface{}{1, 1}},
1053+
&Bucket{BoundVal: []interface{}{9, 9}},
1054+
},
1055+
exp: []sql.HistogramBucket{
1056+
&Bucket{BoundVal: []interface{}{1, 1}},
1057+
},
1058+
},
1059+
{
1060+
types: []sql.Type{types.Int64, types.Int64},
1061+
h1: []sql.HistogramBucket{
1062+
&Bucket{BoundVal: []interface{}{1, 1}},
1063+
&Bucket{BoundVal: []interface{}{9, 9}},
1064+
},
1065+
h2: []sql.HistogramBucket{
1066+
&Bucket{BoundVal: []interface{}{1, 1}},
1067+
&Bucket{BoundVal: []interface{}{9, 9}},
1068+
},
1069+
exp: []sql.HistogramBucket{
1070+
&Bucket{BoundVal: []interface{}{1, 1}},
1071+
&Bucket{BoundVal: []interface{}{9, 9}},
1072+
},
1073+
},
1074+
}
1075+
for _, tt := range tests {
1076+
t.Run(fmt.Sprintf("union: %v and %v", tt.h1, tt.h2), func(t *testing.T) {
1077+
res, err := Intersect(ctx, tt.h1, tt.h2, tt.types)
1078+
require.NoError(t, err)
1079+
require.Equal(t, tt.exp, res)
1080+
})
1081+
}
1082+
}

0 commit comments

Comments
 (0)