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 }