github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/colexec/table_function/unnest_test.go (about)

     1  // Copyright 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 table_function
    16  
    17  import (
    18  	"bytes"
    19  	"testing"
    20  
    21  	"github.com/matrixorigin/matrixone/pkg/common/mpool"
    22  	"github.com/matrixorigin/matrixone/pkg/container/batch"
    23  	"github.com/matrixorigin/matrixone/pkg/container/types"
    24  	"github.com/matrixorigin/matrixone/pkg/container/vector"
    25  	"github.com/matrixorigin/matrixone/pkg/pb/plan"
    26  	"github.com/matrixorigin/matrixone/pkg/sql/colexec/value_scan"
    27  	"github.com/matrixorigin/matrixone/pkg/testutil"
    28  	"github.com/matrixorigin/matrixone/pkg/vm"
    29  	"github.com/matrixorigin/matrixone/pkg/vm/process"
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  type unnestTestCase struct {
    34  	arg      *Argument
    35  	proc     *process.Process
    36  	jsons    []string
    37  	paths    []string
    38  	outers   []bool
    39  	success  bool
    40  	jsonType string
    41  }
    42  
    43  var (
    44  	utc          []unnestTestCase
    45  	defaultAttrs = []string{"col", "seq", "key", "path", "index", "value", "this"}
    46  	//defaultExprs   = []*plan.Expr{
    47  	//	&plan.Expr_C{
    48  	//		C: &plan.Const{
    49  	//			Isnull: false,
    50  	//			Value: &plan.Literal_Sval{}
    51  	//		}
    52  	//	}
    53  	//}
    54  	defaultColDefs = []*plan.ColDef{
    55  		{
    56  			Name: "col",
    57  			Typ: plan.Type{
    58  				Id:          int32(types.T_varchar),
    59  				NotNullable: false,
    60  				Width:       4,
    61  			},
    62  		},
    63  		{
    64  			Name: "seq",
    65  			Typ: plan.Type{
    66  				Id:          int32(types.T_int32),
    67  				NotNullable: false,
    68  			},
    69  		},
    70  		{
    71  			Name: "key",
    72  			Typ: plan.Type{
    73  				Id:          int32(types.T_varchar),
    74  				NotNullable: false,
    75  				Width:       256,
    76  			},
    77  		},
    78  		{
    79  			Name: "path",
    80  			Typ: plan.Type{
    81  				Id:          int32(types.T_varchar),
    82  				NotNullable: false,
    83  				Width:       256,
    84  			},
    85  		},
    86  		{
    87  			Name: "index",
    88  			Typ: plan.Type{
    89  				Id:          int32(types.T_int32),
    90  				NotNullable: false,
    91  			},
    92  		},
    93  		{
    94  			Name: "value",
    95  			Typ: plan.Type{
    96  				Id:          int32(types.T_varchar),
    97  				NotNullable: false,
    98  				Width:       1024,
    99  			},
   100  		},
   101  		{
   102  			Name: "this",
   103  			Typ: plan.Type{
   104  				Id:          int32(types.T_varchar),
   105  				NotNullable: false,
   106  				Width:       1024,
   107  			},
   108  		},
   109  	}
   110  )
   111  
   112  func init() {
   113  	utc = []unnestTestCase{
   114  		newTestCase(mpool.MustNewZero(), defaultAttrs, []string{`{"a":1}`}, []string{`$`}, []bool{false}, "str", true),
   115  		newTestCase(mpool.MustNewZero(), []string{"key", "col"}, []string{`{"a":1}`}, []string{`$`}, []bool{false}, "json", true),
   116  		newTestCase(mpool.MustNewZero(), defaultAttrs, []string{`{"a":1}`, `{"b":1}`}, []string{`$`}, []bool{false}, "json", true),
   117  		newTestCase(mpool.MustNewZero(), defaultAttrs, []string{`{"a":1}`, `{"b":1}`}, []string{`$`, `$`}, []bool{false}, "str", false),
   118  		newTestCase(mpool.MustNewZero(), defaultAttrs, []string{`{"a":1}`, `{"b":1}`}, []string{`$`}, []bool{false, true}, "json", true),
   119  	}
   120  }
   121  
   122  func newTestCase(m *mpool.MPool, attrs []string, jsons, paths []string, outers []bool, jsonType string, success bool) unnestTestCase {
   123  	proc := testutil.NewProcessWithMPool(m)
   124  	colDefs := make([]*plan.ColDef, len(attrs))
   125  	for i := range attrs {
   126  		for j := range defaultColDefs {
   127  			if attrs[i] == defaultColDefs[j].Name {
   128  				colDefs[i] = defaultColDefs[j]
   129  				break
   130  			}
   131  		}
   132  	}
   133  
   134  	ret := unnestTestCase{
   135  		proc: proc,
   136  		arg: &Argument{
   137  			Attrs:    attrs,
   138  			Rets:     colDefs,
   139  			FuncName: "unnest",
   140  			OperatorBase: vm.OperatorBase{
   141  				OperatorInfo: vm.OperatorInfo{
   142  					Idx:     0,
   143  					IsFirst: false,
   144  					IsLast:  false,
   145  				},
   146  			},
   147  		},
   148  		jsons:    jsons,
   149  		paths:    paths,
   150  		outers:   outers,
   151  		success:  success,
   152  		jsonType: jsonType,
   153  	}
   154  
   155  	return ret
   156  }
   157  
   158  func TestUnnestString(t *testing.T) {
   159  	buf := new(bytes.Buffer)
   160  	for _, tc := range utc {
   161  		tc.arg.String(buf)
   162  	}
   163  }
   164  
   165  func TestUnnestCall(t *testing.T) {
   166  	for _, ut := range utc {
   167  
   168  		err := ut.arg.Prepare(ut.proc)
   169  		require.NotNil(t, err)
   170  		var inputBat *batch.Batch
   171  		switch ut.jsonType {
   172  		case "str":
   173  			beforeMem := ut.proc.Mp().CurrNB()
   174  			inputBat, err = makeUnnestBatch(ut.jsons, types.T_varchar, encodeStr, ut.proc)
   175  			require.Nil(t, err)
   176  			ut.arg.Args = makeConstInputExprs(ut.jsons, ut.paths, ut.jsonType, ut.outers)
   177  			err := unnestPrepare(ut.proc, ut.arg)
   178  			require.Nil(t, err)
   179  			result := vm.NewCallResult()
   180  			result.Batch = inputBat
   181  			end, err := unnestCall(0, ut.proc, ut.arg, &result)
   182  			require.Nil(t, err)
   183  			require.False(t, end)
   184  			cleanResult(&result, ut.proc)
   185  			inputBat.Clean(ut.proc.Mp())
   186  			ut.arg.Free(ut.proc, false, nil)
   187  			afterMem := ut.proc.Mp().CurrNB()
   188  			require.Equal(t, beforeMem, afterMem)
   189  		case "json":
   190  			beforeMem := ut.proc.Mp().CurrNB()
   191  			inputBat, err = makeUnnestBatch(ut.jsons, types.T_json, encodeJson, ut.proc)
   192  			require.Nil(t, err)
   193  			ut.arg.Args = makeColExprs(ut.jsonType, ut.paths, ut.outers)
   194  			err := unnestPrepare(ut.proc, ut.arg)
   195  			require.Nil(t, err)
   196  			result := vm.NewCallResult()
   197  			result.Batch = inputBat
   198  			end, err := unnestCall(0, ut.proc, ut.arg, &result)
   199  			require.Nil(t, err)
   200  			require.False(t, end)
   201  			cleanResult(&result, ut.proc)
   202  			inputBat.Clean(ut.proc.Mp())
   203  			ut.arg.Free(ut.proc, false, nil)
   204  			afterMem := ut.proc.Mp().CurrNB()
   205  			require.Equal(t, beforeMem, afterMem)
   206  		}
   207  	}
   208  }
   209  
   210  func makeUnnestBatch(jsons []string, typ types.T, fn func(str string) ([]byte, error), proc *process.Process) (*batch.Batch, error) {
   211  	bat := batch.NewWithSize(1)
   212  	bat.Attrs = []string{"a"}
   213  	for i := range bat.Vecs {
   214  		bat.Vecs[i] = vector.NewVec(types.New(typ, 256, 0))
   215  	}
   216  	bat.Cnt = 1
   217  	for _, json := range jsons {
   218  		bjBytes, err := fn(json)
   219  		if err != nil {
   220  			return nil, err
   221  		}
   222  		err = vector.AppendBytes(bat.GetVector(0), bjBytes, false, proc.Mp())
   223  		if err != nil {
   224  			bat.Clean(proc.Mp())
   225  			return nil, err
   226  		}
   227  	}
   228  	bat.SetRowCount(len(jsons))
   229  	return bat, nil
   230  }
   231  
   232  func encodeJson(json string) ([]byte, error) {
   233  	bj, err := types.ParseStringToByteJson(json)
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  	return types.EncodeJson(bj)
   238  }
   239  func encodeStr(json string) ([]byte, error) {
   240  	return []byte(json), nil
   241  }
   242  
   243  func makeConstInputExprs(jsons, paths []string, jsonType string, outers []bool) []*plan.Expr {
   244  	ret := make([]*plan.Expr, 3)
   245  	typeId := int32(types.T_varchar)
   246  	if jsonType == "json" {
   247  		typeId = int32(types.T_json)
   248  	}
   249  	ret[0] = &plan.Expr{
   250  		Typ: plan.Type{
   251  			Id:    typeId,
   252  			Width: 256,
   253  		},
   254  		Expr: &plan.Expr_Lit{
   255  			Lit: &plan.Literal{
   256  				Value: &plan.Literal_Sval{
   257  					Sval: jsons[0],
   258  				},
   259  			},
   260  		},
   261  	}
   262  	ret = appendOtherExprs(ret, paths, outers)
   263  	return ret
   264  }
   265  
   266  func makeColExprs(jsonType string, paths []string, outers []bool) []*plan.Expr {
   267  	ret := make([]*plan.Expr, 3)
   268  	typeId := int32(types.T_varchar)
   269  	if jsonType == "json" {
   270  		typeId = int32(types.T_json)
   271  	}
   272  	ret[0] = &plan.Expr{
   273  		Typ: plan.Type{
   274  			Id: typeId,
   275  		},
   276  		Expr: &plan.Expr_Col{
   277  			Col: &plan.ColRef{
   278  				ColPos: 0,
   279  			},
   280  		},
   281  	}
   282  	ret = appendOtherExprs(ret, paths, outers)
   283  	return ret
   284  }
   285  
   286  func appendOtherExprs(ret []*plan.Expr, paths []string, outers []bool) []*plan.Expr {
   287  	ret[1] = &plan.Expr{
   288  		Typ: plan.Type{
   289  			Id:    int32(types.T_varchar),
   290  			Width: 256,
   291  		},
   292  		Expr: &plan.Expr_Lit{
   293  			Lit: &plan.Literal{
   294  				Value: &plan.Literal_Sval{
   295  					Sval: paths[0],
   296  				},
   297  			},
   298  		},
   299  	}
   300  	ret[2] = &plan.Expr{
   301  		Typ: plan.Type{
   302  			Id: int32(types.T_bool),
   303  		},
   304  		Expr: &plan.Expr_Lit{
   305  			Lit: &plan.Literal{
   306  				Value: &plan.Literal_Bval{
   307  					Bval: outers[0],
   308  				},
   309  			},
   310  		},
   311  	}
   312  	return ret
   313  }
   314  
   315  func resetChildren(arg *Argument, bat *batch.Batch) {
   316  	arg.SetChildren(
   317  		[]vm.Operator{
   318  			&value_scan.Argument{
   319  				Batchs: []*batch.Batch{bat},
   320  			},
   321  		})
   322  }
   323  
   324  func cleanResult(result *vm.CallResult, proc *process.Process) {
   325  	if result.Batch != nil {
   326  		result.Batch.Clean(proc.Mp())
   327  		result.Batch = nil
   328  	}
   329  }