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