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 }