github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/plan/function/function_test.go (about) 1 // Copyright 2021 - 2022 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package function 16 17 import ( 18 "fmt" 19 "testing" 20 "time" 21 22 "github.com/matrixorigin/matrixone/pkg/container/vector" 23 "github.com/stretchr/testify/assert" 24 25 "github.com/matrixorigin/matrixone/pkg/container/types" 26 "github.com/matrixorigin/matrixone/pkg/testutil" 27 "github.com/stretchr/testify/require" 28 ) 29 30 func Test_fixedTypeCastRule1(t *testing.T) { 31 inputs := []struct { 32 shouldCast bool 33 in [2]types.Type 34 want [2]types.Type 35 }{ 36 { 37 shouldCast: true, 38 in: [2]types.Type{types.T_int64.ToType(), types.T_int32.ToType()}, 39 want: [2]types.Type{types.T_int64.ToType(), types.T_int64.ToType()}, 40 }, 41 42 { 43 shouldCast: false, 44 in: [2]types.Type{types.T_int64.ToType(), types.T_int64.ToType()}, 45 }, 46 47 { 48 shouldCast: true, 49 in: [2]types.Type{ 50 {Oid: types.T_decimal64, Width: 38, Size: 16, Scale: 6}, 51 {Oid: types.T_decimal128, Width: 38, Size: 16, Scale: 4}, 52 }, 53 want: [2]types.Type{ 54 {Oid: types.T_decimal128, Width: 38, Size: 16, Scale: 6}, 55 {Oid: types.T_decimal128, Width: 38, Size: 16, Scale: 4}, 56 }, 57 }, 58 59 // special rule, null + null 60 // we just cast it as int64 + int64 61 { 62 shouldCast: true, 63 in: [2]types.Type{types.T_any.ToType(), types.T_any.ToType()}, 64 want: [2]types.Type{types.T_int64.ToType(), types.T_int64.ToType()}, 65 }, 66 } 67 68 for i, in := range inputs { 69 msg := fmt.Sprintf("i = %d", i) 70 71 cast, t1, t2 := fixedTypeCastRule1(in.in[0], in.in[1]) 72 require.Equal(t, in.shouldCast, cast, msg) 73 if in.shouldCast { 74 require.Equal(t, in.want[0], t1, msg) 75 require.Equal(t, in.want[1], t2, msg) 76 } 77 } 78 } 79 80 func Test_fixedTypeCastRule2(t *testing.T) { 81 inputs := []struct { 82 shouldCast bool 83 in [2]types.Type 84 want [2]types.Type 85 }{ 86 { 87 shouldCast: true, 88 in: [2]types.Type{types.T_int64.ToType(), types.T_int32.ToType()}, 89 want: [2]types.Type{types.T_float64.ToType(), types.T_float64.ToType()}, 90 }, 91 92 { 93 shouldCast: false, 94 in: [2]types.Type{types.T_float64.ToType(), types.T_float64.ToType()}, 95 }, 96 97 { 98 shouldCast: true, 99 in: [2]types.Type{ 100 {Oid: types.T_decimal64, Width: 38, Size: 16, Scale: 6}, 101 types.T_float64.ToType(), 102 }, 103 want: [2]types.Type{types.T_float64.ToTypeWithScale(6), types.T_float64.ToType()}, 104 }, 105 106 { 107 shouldCast: true, 108 in: [2]types.Type{ 109 {Oid: types.T_decimal64, Width: 38, Size: 16, Scale: 6}, 110 {Oid: types.T_decimal128, Width: 38, Size: 16, Scale: 4}, 111 }, 112 want: [2]types.Type{ 113 {Oid: types.T_decimal128, Width: 38, Size: 16, Scale: 6}, 114 {Oid: types.T_decimal128, Width: 38, Size: 16, Scale: 4}, 115 }, 116 }, 117 118 // special rule, null / null 119 // we just cast it as float64 / float64 120 { 121 shouldCast: true, 122 in: [2]types.Type{types.T_int64.ToType(), types.T_int32.ToType()}, 123 want: [2]types.Type{types.T_float64.ToType(), types.T_float64.ToType()}, 124 }, 125 } 126 127 for i, in := range inputs { 128 msg := fmt.Sprintf("i = %d", i) 129 130 cast, t1, t2 := fixedTypeCastRule2(in.in[0], in.in[1]) 131 require.Equal(t, in.shouldCast, cast, msg) 132 if in.shouldCast { 133 require.Equal(t, in.want[0], t1, msg) 134 require.Equal(t, in.want[1], t2, msg) 135 } 136 } 137 } 138 139 func Test_GetFunctionByName(t *testing.T) { 140 type fInput struct { 141 name string 142 args []types.Type 143 144 // expected 145 shouldErr bool 146 147 requireFid int32 148 requireOid int32 149 150 shouldCast bool 151 requireTyp []types.Type 152 153 requireRet types.Type 154 } 155 156 cs := []fInput{ 157 { 158 name: "+", args: []types.Type{types.T_int8.ToType(), types.T_int16.ToType()}, 159 shouldErr: false, 160 requireFid: PLUS, requireOid: 0, 161 shouldCast: true, requireTyp: []types.Type{types.T_int16.ToType(), types.T_int16.ToType()}, 162 requireRet: types.T_int16.ToType(), 163 }, 164 165 { 166 name: "+", args: []types.Type{types.T_int64.ToType(), types.T_int64.ToType()}, 167 shouldErr: false, 168 requireFid: PLUS, requireOid: 0, 169 shouldCast: false, 170 requireRet: types.T_int64.ToType(), 171 }, 172 173 { 174 name: "/", args: []types.Type{types.T_int8.ToType(), types.T_int16.ToType()}, 175 shouldErr: false, 176 requireFid: DIV, requireOid: 0, 177 shouldCast: true, requireTyp: []types.Type{types.T_float64.ToType(), types.T_float64.ToType()}, 178 requireRet: types.T_float64.ToType(), 179 }, 180 181 { 182 name: "internal_numeric_scale", args: []types.Type{types.T_char.ToType()}, 183 shouldErr: false, 184 requireFid: INTERNAL_NUMERIC_SCALE, requireOid: 0, 185 shouldCast: true, requireTyp: []types.Type{types.T_varchar.ToType()}, 186 requireRet: types.T_int64.ToType(), 187 }, 188 189 { 190 name: "internal_numeric_scale", args: []types.Type{types.T_char.ToType(), types.T_int64.ToType()}, 191 shouldErr: true, 192 }, 193 194 { 195 name: "iff", args: []types.Type{types.T_bool.ToType(), types.T_any.ToType(), types.T_int64.ToType()}, 196 shouldErr: false, 197 requireFid: IFF, requireOid: 0, 198 shouldCast: true, requireTyp: []types.Type{types.T_bool.ToType(), types.T_int64.ToType(), types.T_int64.ToType()}, 199 requireRet: types.T_int64.ToType(), 200 }, 201 } 202 203 proc := testutil.NewProcess() 204 for i, c := range cs { 205 msg := fmt.Sprintf("%dth case", i) 206 207 get, err := GetFunctionByName(proc.Ctx, c.name, c.args) 208 if c.shouldErr { 209 require.True(t, err != nil, msg) 210 } else { 211 require.NoError(t, err, msg) 212 require.Equal(t, c.requireFid, get.fid, msg) 213 require.Equal(t, c.requireOid, get.overloadId, msg) 214 require.Equal(t, c.shouldCast, get.needCast, msg) 215 if c.shouldCast { 216 require.Equal(t, len(c.requireTyp), len(get.targetTypes), msg) 217 for j := range c.requireTyp { 218 require.Equal(t, c.requireTyp[j], get.targetTypes[j], msg) 219 } 220 } 221 require.Equal(t, c.requireRet, get.retType, msg) 222 } 223 } 224 } 225 226 func TestGetFunctionIsWinfunByName(t *testing.T) { 227 assert.Equal(t, true, GetFunctionIsWinFunByName("rank")) 228 assert.Equal(t, false, GetFunctionIsWinFunByName("floor")) 229 } 230 231 func TestRunFunctionDirectly(t *testing.T) { 232 // fold case. 233 { 234 proc := testutil.NewProcess() 235 v0, err1 := vector.NewConstFixed(types.T_bool.ToType(), true, 10, proc.Mp()) 236 require.NoError(t, err1) 237 v1, err2 := vector.NewConstFixed(types.T_bool.ToType(), true, 10, proc.Mp()) 238 require.NoError(t, err2) 239 inputs := []*vector.Vector{v0, v1} 240 startMp := proc.Mp().CurrNB() 241 242 v, err := RunFunctionDirectly(proc, AndFunctionEncodedID, inputs, 10) 243 require.NoError(t, err) 244 245 require.Equal(t, 10, v.Length()) 246 wrapper := vector.GenerateFunctionFixedTypeParameter[bool](v) 247 for i := 0; i < 10; i++ { 248 value, null := wrapper.GetValue(uint64(i)) 249 require.Equal(t, false, null) 250 require.Equal(t, true, value) 251 } 252 253 v.Free(proc.Mp()) 254 proc.FreeVectors() 255 require.Equal(t, startMp, proc.Mp().CurrNB()) 256 } 257 258 // non-fold case. 259 { 260 proc := testutil.NewProcess() 261 inputs := []*vector.Vector{ 262 testutil.NewVector(2, types.T_bool.ToType(), proc.Mp(), false, []bool{true, true}), 263 testutil.NewVector(2, types.T_bool.ToType(), proc.Mp(), false, []bool{true, true}), 264 } 265 startMp := proc.Mp().CurrNB() 266 267 v, err := RunFunctionDirectly(proc, AndFunctionEncodedID, inputs, 2) 268 require.NoError(t, err) 269 270 require.Equal(t, 2, v.Length()) 271 wrapper := vector.GenerateFunctionFixedTypeParameter[bool](v) 272 for i := 0; i < 2; i++ { 273 value, null := wrapper.GetValue(uint64(i)) 274 require.Equal(t, false, null) 275 require.Equal(t, true, value) 276 } 277 278 v.Free(proc.Mp()) 279 require.Equal(t, startMp, proc.Mp().CurrNB()) 280 } 281 } 282 283 func TestCastNanoToTimestamp(t *testing.T) { 284 inputs := []string{ 285 "2021-04-13 08:00:00.000000099", 286 "2021-04-13 08:00:00.000000101", 287 "2021-04-13 08:00:00", 288 } 289 outputs := make([]int64, len(inputs)) 290 for i, in := range inputs { 291 outputs[i] = convertStringToTimeUtcNano(in) 292 } 293 294 testCases := initCastNanoToTimestampTestCase(inputs, outputs) 295 296 proc := testutil.NewProcess() 297 for _, tc := range testCases { 298 fcTC := testutil.NewFunctionTestCase(proc, tc.inputs, tc.expect, CastNanoToTimestamp) 299 s, info := fcTC.Run() 300 require.True(t, s, fmt.Sprintf("err info is '%s'", info)) 301 } 302 303 } 304 305 func initCastNanoToTimestampTestCase(inputs []string, outputs []int64) []tcTemp { 306 res := make([]tcTemp, len(inputs)) 307 for i := range inputs { 308 res[i] = tcTemp{ 309 info: fmt.Sprintf("case %d", i), 310 typ: types.T_int64, 311 inputs: []testutil.FunctionTestInput{ 312 testutil.NewFunctionTestInput(types.T_int64.ToType(), 313 []int64{outputs[i]}, 314 []bool{false}), 315 }, 316 expect: testutil.NewFunctionTestResult(types.T_varchar.ToType(), false, 317 []string{inputs[i]}, 318 []bool{false}), 319 } 320 } 321 return res 322 } 323 324 func convertStringToTimeUtcNano(str string) int64 { 325 ts, _ := time.Parse("2006-01-02 15:04:05.999999999", str) 326 return ts.UTC().UnixNano() 327 }