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  }