Skip to content

Commit

Permalink
fix bug: prefix_xx get incorrect result (#21075)
Browse files Browse the repository at this point in the history
fix bug: prefix_xx get incorrect result

Approved by: @qingxinhome, @sukki37
  • Loading branch information
ouyuanning authored Jan 6, 2025
1 parent b99fd61 commit c883119
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 15 deletions.
78 changes: 63 additions & 15 deletions pkg/sql/plan/function/func_prefix.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ func PrefixEq(parameters []*vector.Vector, result vector.FunctionResultWrapper,
res := vector.MustFixedColWithTypeCheck[bool](result.GetResultVector())

lcol, larea := vector.MustVarlenaRawData(lvec)
lvecHasNull := lvec.HasNull()

if lvec.GetSorted() {
if lvec.GetSorted() && !lvecHasNull {
lowerBound := sort.Search(len(lcol), func(i int) bool {
return bytes.Compare(rval, lcol[i].GetByteSlice(larea)) <= 0
})
Expand All @@ -55,8 +56,21 @@ func PrefixEq(parameters []*vector.Vector, result vector.FunctionResultWrapper,
res[i] = false
}
} else {
for i := 0; i < length; i++ {
res[i] = bytes.HasPrefix(lcol[i].GetByteSlice(larea), rval)
if lvecHasNull {
lNulls := lvec.GetNulls()
rNulls := result.GetResultVector().GetNulls()
for i := uint64(0); i < uint64(length); i++ {
if lNulls.Contains(i) {
res[i] = false
rNulls.Add(i)
} else {
res[i] = bytes.HasPrefix(lcol[i].GetByteSlice(larea), rval)
}
}
} else {
for i := 0; i < length; i++ {
res[i] = bytes.HasPrefix(lcol[i].GetByteSlice(larea), rval)
}
}
}

Expand All @@ -70,8 +84,9 @@ func PrefixBetween(parameters []*vector.Vector, result vector.FunctionResultWrap
res := vector.MustFixedColWithTypeCheck[bool](result.GetResultVector())

icol, iarea := vector.MustVarlenaRawData(ivec)
ivecHasNull := ivec.HasNull()

if ivec.GetSorted() {
if ivec.GetSorted() && !ivecHasNull {
lowerBound := sort.Search(len(icol), func(i int) bool {
return types.PrefixCompare(icol[i].GetByteSlice(iarea), lval) >= 0
})
Expand All @@ -90,9 +105,23 @@ func PrefixBetween(parameters []*vector.Vector, result vector.FunctionResultWrap
res[i] = false
}
} else {
for i := 0; i < length; i++ {
val := icol[i].GetByteSlice(iarea)
res[i] = types.PrefixCompare(val, lval) >= 0 && types.PrefixCompare(val, rval) <= 0
if ivecHasNull {
iNulls := ivec.GetNulls()
rNulls := result.GetResultVector().GetNulls()
for i := uint64(0); i < uint64(length); i++ {
if iNulls.Contains(i) {
res[i] = false
rNulls.Add(i)
} else {
val := icol[i].GetByteSlice(iarea)
res[i] = types.PrefixCompare(val, lval) >= 0 && types.PrefixCompare(val, rval) <= 0
}
}
} else {
for i := 0; i < length; i++ {
val := icol[i].GetByteSlice(iarea)
res[i] = types.PrefixCompare(val, lval) >= 0 && types.PrefixCompare(val, rval) <= 0
}
}
}

Expand Down Expand Up @@ -132,8 +161,9 @@ func (op *implPrefixIn) doPrefixIn(parameters []*vector.Vector, result vector.Fu
res := vector.MustFixedColWithTypeCheck[bool](result.GetResultVector())

lcol, larea := vector.MustVarlenaRawData(lvec)
lvecHasNull := lvec.HasNull()

if lvec.GetSorted() {
if lvec.GetSorted() && !lvecHasNull {
rval := op.vals[0]
rpos := 0
rlen := len(op.vals)
Expand All @@ -155,13 +185,31 @@ func (op *implPrefixIn) doPrefixIn(parameters []*vector.Vector, result vector.Fu
res[i] = bytes.HasPrefix(lval, rval)
}
} else {
for i := 0; i < length; i++ {
lval := lcol[i].GetByteSlice(larea)
rpos, _ := sort.Find(len(op.vals), func(j int) int {
return types.PrefixCompare(lval, op.vals[j])
})

res[i] = rpos < len(op.vals) && bytes.HasPrefix(lval, op.vals[rpos])
if lvecHasNull {
lNulls := lvec.GetNulls()
rNulls := result.GetResultVector().GetNulls()
for i := uint64(0); i < uint64(length); i++ {
if lNulls.Contains(i) {
res[i] = false
rNulls.Add(i)
} else {
lval := lcol[i].GetByteSlice(larea)
rpos, _ := sort.Find(len(op.vals), func(j int) int {
return types.PrefixCompare(lval, op.vals[j])
})

res[i] = rpos < len(op.vals) && bytes.HasPrefix(lval, op.vals[rpos])
}
}
} else {
for i := 0; i < length; i++ {
lval := lcol[i].GetByteSlice(larea)
rpos, _ := sort.Find(len(op.vals), func(j int) int {
return types.PrefixCompare(lval, op.vals[j])
})

res[i] = rpos < len(op.vals) && bytes.HasPrefix(lval, op.vals[rpos])
}
}
}

Expand Down
132 changes: 132 additions & 0 deletions pkg/sql/plan/function/func_prefix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// Copyright 2021 Matrix Origin
//
// 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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package function

import (
"testing"

"github.com/matrixorigin/matrixone/pkg/container/types"
"github.com/matrixorigin/matrixone/pkg/testutil"
"github.com/stretchr/testify/require"
)

func TestPrefixEq(t *testing.T) {
tcs := []tcTemp{
{
info: "& test prefix_eq",
inputs: []FunctionTestInput{
NewFunctionTestInput(types.T_varchar.ToType(),
[]string{"aa12", "bb22", "aa33", "aa44"}, []bool{false, false, false, false}),
NewFunctionTestInput(types.T_varchar.ToType(),
[]string{"aa"}, []bool{false, false, false}),
},
expect: NewFunctionTestResult(types.T_bool.ToType(), false,
[]bool{true, false, true, true}, []bool{false, false, false, false}),
},
{
info: "& test prefix_eq with null",
inputs: []FunctionTestInput{
NewFunctionTestInput(types.T_varchar.ToType(),
[]string{"aa12", "bb22", "aa33", "aa44"}, []bool{false, false, true, false}),
NewFunctionTestInput(types.T_varchar.ToType(),
[]string{"aa"}, []bool{false, false, false}),
},
expect: NewFunctionTestResult(types.T_bool.ToType(), false,
[]bool{true, false, false, true}, []bool{false, false, true, false}),
},
}

proc := testutil.NewProcess()
for _, tc := range tcs {
fcTC := NewFunctionTestCase(proc,
tc.inputs, tc.expect, PrefixEq)
s, info := fcTC.Run()
require.True(t, s, info, tc.info)
}
}

func TestPrefixIn(t *testing.T) {
tcs := []tcTemp{
{
info: "& test prefix_in",
inputs: []FunctionTestInput{
NewFunctionTestInput(types.T_varchar.ToType(),
[]string{"aa12", "ab23", "bb34"}, []bool{false, false, false}),
NewFunctionTestInput(types.T_varchar.ToType(),
[]string{"aa", "bb", "ss"}, []bool{false, false, false}),
},
expect: NewFunctionTestResult(types.T_bool.ToType(), false,
[]bool{true, false, true}, []bool{false, false, false}),
},
{
info: "& test prefix_in with null",
inputs: []FunctionTestInput{
NewFunctionTestInput(types.T_varchar.ToType(),
[]string{"aa12", "ab23", "bb34"}, []bool{false, false, true}),
NewFunctionTestInput(types.T_varchar.ToType(),
[]string{"aa", "bb", "ss"}, []bool{false, false, true}),
},
expect: NewFunctionTestResult(types.T_bool.ToType(), false,
[]bool{true, false, false}, []bool{false, false, true}),
},
}

proc := testutil.NewProcess()
for _, tc := range tcs {
fcTC := NewFunctionTestCase(proc,
tc.inputs, tc.expect, newImplPrefixIn().doPrefixIn)
s, info := fcTC.Run()
require.True(t, s, info, tc.info)
}
}

func TestPrefixBetween(t *testing.T) {
tcs := []tcTemp{
{
info: "& test prefix_in",
inputs: []FunctionTestInput{
NewFunctionTestInput(types.T_varchar.ToType(),
[]string{"aa11", "aa22", "bb22", "ss34"}, []bool{false, false, false, false}),
NewFunctionTestInput(types.T_varchar.ToType(),
[]string{"aa"}, []bool{false}),
NewFunctionTestInput(types.T_varchar.ToType(),
[]string{"kk"}, []bool{false}),
},
expect: NewFunctionTestResult(types.T_bool.ToType(), false,
[]bool{true, true, true, false}, []bool{false, false, false, false}),
},
{
info: "& test prefix_in with null",
inputs: []FunctionTestInput{
NewFunctionTestInput(types.T_varchar.ToType(),
[]string{"aa11", "aa22", "bb22", "ss34"}, []bool{false, true, false, false}),
NewFunctionTestInput(types.T_varchar.ToType(),
[]string{"aa"}, []bool{false}),
NewFunctionTestInput(types.T_varchar.ToType(),
[]string{"kk"}, []bool{false}),
},
expect: NewFunctionTestResult(types.T_bool.ToType(), false,
[]bool{true, false, true, false}, []bool{false, true, false, false}),
},
}

proc := testutil.NewProcess()
for _, tc := range tcs {
fcTC := NewFunctionTestCase(proc,
tc.inputs, tc.expect, PrefixBetween)
s, info := fcTC.Run()
require.True(t, s, info, tc.info)
}
}

0 comments on commit c883119

Please sign in to comment.