github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/petri/acyclic/causet/embedded/planbuilder_test.go (about) 1 // Copyright 2020 WHTCORPS INC, Inc. 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 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package embedded 15 16 import ( 17 "context" 18 "fmt" 19 "reflect" 20 "strings" 21 "unsafe" 22 _ "unsafe" // required by go:linkname 23 24 "github.com/whtcorpsinc/BerolinaSQL" 25 "github.com/whtcorpsinc/BerolinaSQL/allegrosql" 26 "github.com/whtcorpsinc/BerolinaSQL/ast" 27 "github.com/whtcorpsinc/BerolinaSQL/perceptron" 28 . "github.com/whtcorpsinc/check" 29 "github.com/whtcorpsinc/errors" 30 "github.com/whtcorpsinc/milevadb/causet/property" 31 "github.com/whtcorpsinc/milevadb/causet/soliton" 32 "github.com/whtcorpsinc/milevadb/ekv" 33 "github.com/whtcorpsinc/milevadb/memex" 34 "github.com/whtcorpsinc/milevadb/memex/aggregation" 35 "github.com/whtcorpsinc/milevadb/soliton/chunk" 36 "github.com/whtcorpsinc/milevadb/soliton/hint" 37 "github.com/whtcorpsinc/milevadb/soliton/mock" 38 "github.com/whtcorpsinc/milevadb/statistics" 39 "github.com/whtcorpsinc/milevadb/types" 40 ) 41 42 var _ = Suite(&testCausetBuilderSuite{}) 43 44 func (s *testCausetBuilderSuite) SetUpSuite(c *C) { 45 } 46 47 type testCausetBuilderSuite struct { 48 } 49 50 func (s *testCausetBuilderSuite) TestShow(c *C) { 51 node := &ast.ShowStmt{} 52 tps := []ast.ShowStmtType{ 53 ast.ShowEngines, 54 ast.ShowDatabases, 55 ast.ShowBlocks, 56 ast.ShowBlockStatus, 57 ast.ShowDeferredCausets, 58 ast.ShowWarnings, 59 ast.ShowCharset, 60 ast.ShowVariables, 61 ast.ShowStatus, 62 ast.ShowDefCauslation, 63 ast.ShowCreateBlock, 64 ast.ShowCreateUser, 65 ast.ShowGrants, 66 ast.ShowTriggers, 67 ast.ShowProcedureStatus, 68 ast.ShowIndex, 69 ast.ShowProcessList, 70 ast.ShowCreateDatabase, 71 ast.ShowEvents, 72 ast.ShowMasterStatus, 73 ast.ShowBackups, 74 ast.ShowRestores, 75 } 76 for _, tp := range tps { 77 node.Tp = tp 78 schemaReplicant, _ := buildShowSchema(node, false, false) 79 for _, col := range schemaReplicant.DeferredCausets { 80 c.Assert(col.RetType.Flen, Greater, 0) 81 } 82 } 83 } 84 85 func (s *testCausetBuilderSuite) TestGetPathByIndexName(c *C) { 86 tblInfo := &perceptron.BlockInfo{ 87 Indices: make([]*perceptron.IndexInfo, 0), 88 PKIsHandle: true, 89 } 90 91 accessPath := []*soliton.AccessPath{ 92 {IsIntHandlePath: true}, 93 {Index: &perceptron.IndexInfo{Name: perceptron.NewCIStr("idx")}}, 94 } 95 96 path := getPathByIndexName(accessPath, perceptron.NewCIStr("idx"), tblInfo) 97 c.Assert(path, NotNil) 98 c.Assert(path, Equals, accessPath[1]) 99 100 path = getPathByIndexName(accessPath, perceptron.NewCIStr("primary"), tblInfo) 101 c.Assert(path, NotNil) 102 c.Assert(path, Equals, accessPath[0]) 103 104 path = getPathByIndexName(accessPath, perceptron.NewCIStr("not exists"), tblInfo) 105 c.Assert(path, IsNil) 106 107 tblInfo = &perceptron.BlockInfo{ 108 Indices: make([]*perceptron.IndexInfo, 0), 109 PKIsHandle: false, 110 } 111 112 path = getPathByIndexName(accessPath, perceptron.NewCIStr("primary"), tblInfo) 113 c.Assert(path, IsNil) 114 } 115 116 func (s *testCausetBuilderSuite) TestRewriterPool(c *C) { 117 builder := NewCausetBuilder(MockContext(), nil, &hint.BlockHintProcessor{}) 118 119 // Make sure CausetBuilder.getExpressionRewriter() provides clean rewriter from pool. 120 // First, pick one rewriter from the pool and make it dirty. 121 builder.rewriterCounter++ 122 dirtyRewriter := builder.getExpressionRewriter(context.TODO(), nil) 123 dirtyRewriter.asScalar = true 124 dirtyRewriter.aggrMap = make(map[*ast.AggregateFuncExpr]int) 125 dirtyRewriter.preprocess = func(ast.Node) ast.Node { return nil } 126 dirtyRewriter.insertCauset = &Insert{} 127 dirtyRewriter.disableFoldCounter = 1 128 dirtyRewriter.ctxStack = make([]memex.Expression, 2) 129 dirtyRewriter.ctxNameStk = make([]*types.FieldName, 2) 130 builder.rewriterCounter-- 131 // Then, pick again and check if it's cleaned up. 132 builder.rewriterCounter++ 133 cleanRewriter := builder.getExpressionRewriter(context.TODO(), nil) 134 c.Assert(cleanRewriter, Equals, dirtyRewriter) // Rewriter should be reused. 135 c.Assert(cleanRewriter.asScalar, Equals, false) 136 c.Assert(cleanRewriter.aggrMap, IsNil) 137 c.Assert(cleanRewriter.preprocess, IsNil) 138 c.Assert(cleanRewriter.insertCauset, IsNil) 139 c.Assert(cleanRewriter.disableFoldCounter, Equals, 0) 140 c.Assert(len(cleanRewriter.ctxStack), Equals, 0) 141 builder.rewriterCounter-- 142 } 143 144 func (s *testCausetBuilderSuite) TestDisableFold(c *C) { 145 // Functions like BENCHMARK() shall not be folded into result 0, 146 // but normal outer function with constant args should be folded. 147 // Types of memex and first layer of args will be validated. 148 cases := []struct { 149 ALLEGROALLEGROSQL string 150 Expected memex.Expression 151 Args []memex.Expression 152 }{ 153 {`select sin(length("abc"))`, &memex.Constant{}, nil}, 154 {`select benchmark(3, sin(123))`, &memex.ScalarFunction{}, []memex.Expression{ 155 &memex.Constant{}, 156 &memex.ScalarFunction{}, 157 }}, 158 {`select pow(length("abc"), benchmark(3, sin(123)))`, &memex.ScalarFunction{}, []memex.Expression{ 159 &memex.Constant{}, 160 &memex.ScalarFunction{}, 161 }}, 162 } 163 164 ctx := MockContext() 165 for _, t := range cases { 166 st, err := BerolinaSQL.New().ParseOneStmt(t.ALLEGROALLEGROSQL, "", "") 167 c.Assert(err, IsNil) 168 stmt := st.(*ast.SelectStmt) 169 expr := stmt.Fields.Fields[0].Expr 170 171 builder := NewCausetBuilder(ctx, nil, &hint.BlockHintProcessor{}) 172 builder.rewriterCounter++ 173 rewriter := builder.getExpressionRewriter(context.TODO(), nil) 174 c.Assert(rewriter, NotNil) 175 c.Assert(rewriter.disableFoldCounter, Equals, 0) 176 rewritenExpression, _, err := builder.rewriteExprNode(rewriter, expr, true) 177 c.Assert(err, IsNil) 178 c.Assert(rewriter.disableFoldCounter, Equals, 0) // Make sure the counter is reduced to 0 in the end. 179 builder.rewriterCounter-- 180 181 c.Assert(rewritenExpression, FitsTypeOf, t.Expected) 182 for i, expectedArg := range t.Args { 183 rewritenArg := memex.GetFuncArg(rewritenExpression, i) 184 c.Assert(rewritenArg, FitsTypeOf, expectedArg) 185 } 186 } 187 } 188 189 func (s *testCausetBuilderSuite) TestDeepClone(c *C) { 190 tp := types.NewFieldType(allegrosql.TypeLonglong) 191 expr := &memex.DeferredCauset{RetType: tp} 192 byItems := []*soliton.ByItems{{Expr: expr}} 193 sort1 := &PhysicalSort{ByItems: byItems} 194 sort2 := &PhysicalSort{ByItems: byItems} 195 checkDeepClone := func(p1, p2 PhysicalCauset) error { 196 whiteList := []string{"*property.StatsInfo", "*stochastikctx.Context", "*mock.Context"} 197 return checkDeepClonedCore(reflect.ValueOf(p1), reflect.ValueOf(p2), typeName(reflect.TypeOf(p1)), whiteList, nil) 198 } 199 c.Assert(checkDeepClone(sort1, sort2), ErrorMatches, "invalid slice pointers, path PhysicalSort.ByItems") 200 201 byItems2 := []*soliton.ByItems{{Expr: expr}} 202 sort2.ByItems = byItems2 203 c.Assert(checkDeepClone(sort1, sort2), ErrorMatches, "same pointer, path PhysicalSort.ByItems.*Expression") 204 205 expr2 := &memex.DeferredCauset{RetType: tp} 206 byItems2[0].Expr = expr2 207 c.Assert(checkDeepClone(sort1, sort2), ErrorMatches, "same pointer, path PhysicalSort.ByItems.*Expression.FieldType") 208 209 expr2.RetType = types.NewFieldType(allegrosql.TypeString) 210 c.Assert(checkDeepClone(sort1, sort2), ErrorMatches, "different values, path PhysicalSort.ByItems.*Expression.FieldType.uint8") 211 212 expr2.RetType = types.NewFieldType(allegrosql.TypeLonglong) 213 c.Assert(checkDeepClone(sort1, sort2), IsNil) 214 } 215 216 func (s *testCausetBuilderSuite) TestPhysicalCausetClone(c *C) { 217 ctx := mock.NewContext() 218 col, cst := &memex.DeferredCauset{RetType: types.NewFieldType(allegrosql.TypeString)}, &memex.Constant{RetType: types.NewFieldType(allegrosql.TypeLonglong)} 219 stats := &property.StatsInfo{RowCount: 1000} 220 schemaReplicant := memex.NewSchema(col) 221 tblInfo := &perceptron.BlockInfo{} 222 idxInfo := &perceptron.IndexInfo{} 223 hist := &statistics.Histogram{Bounds: chunk.New(nil, 0, 0)} 224 aggDesc1, err := aggregation.NewAggFuncDesc(ctx, ast.AggFuncAvg, []memex.Expression{col}, false) 225 c.Assert(err, IsNil) 226 aggDesc2, err := aggregation.NewAggFuncDesc(ctx, ast.AggFuncCount, []memex.Expression{cst}, true) 227 c.Assert(err, IsNil) 228 aggDescs := []*aggregation.AggFuncDesc{aggDesc1, aggDesc2} 229 230 // causet scan 231 blockScan := &PhysicalBlockScan{ 232 AccessCondition: []memex.Expression{col, cst}, 233 Block: tblInfo, 234 PkDefCauss: []*memex.DeferredCauset{col}, 235 Hist: hist, 236 } 237 blockScan = blockScan.Init(ctx, 0) 238 blockScan.SetSchema(schemaReplicant) 239 c.Assert(checkPhysicalCausetClone(blockScan), IsNil) 240 241 // causet reader 242 blockReader := &PhysicalBlockReader{ 243 blockCauset: blockScan, 244 BlockCausets: []PhysicalCauset{blockScan}, 245 StoreType: ekv.TiFlash, 246 } 247 blockReader = blockReader.Init(ctx, 0) 248 c.Assert(checkPhysicalCausetClone(blockReader), IsNil) 249 250 // index scan 251 indexScan := &PhysicalIndexScan{ 252 AccessCondition: []memex.Expression{col, cst}, 253 Block: tblInfo, 254 Index: idxInfo, 255 Hist: hist, 256 dataSourceSchema: schemaReplicant, 257 } 258 indexScan = indexScan.Init(ctx, 0) 259 indexScan.SetSchema(schemaReplicant) 260 c.Assert(checkPhysicalCausetClone(indexScan), IsNil) 261 262 // index reader 263 indexReader := &PhysicalIndexReader{ 264 indexCauset: indexScan, 265 IndexCausets: []PhysicalCauset{indexScan}, 266 OutputDeferredCausets: []*memex.DeferredCauset{col, col}, 267 } 268 indexReader = indexReader.Init(ctx, 0) 269 c.Assert(checkPhysicalCausetClone(indexReader), IsNil) 270 271 // index lookup 272 indexLookup := &PhysicalIndexLookUpReader{ 273 IndexCausets: []PhysicalCauset{indexReader}, 274 indexCauset: indexScan, 275 BlockCausets: []PhysicalCauset{blockReader}, 276 blockCauset: blockScan, 277 ExtraHandleDefCaus: col, 278 PushedLimit: &PushedDownLimit{1, 2}, 279 } 280 indexLookup = indexLookup.Init(ctx, 0) 281 c.Assert(checkPhysicalCausetClone(indexLookup), IsNil) 282 283 // selection 284 sel := &PhysicalSelection{Conditions: []memex.Expression{col, cst}} 285 sel = sel.Init(ctx, stats, 0) 286 c.Assert(checkPhysicalCausetClone(sel), IsNil) 287 288 // projection 289 proj := &PhysicalProjection{Exprs: []memex.Expression{col, cst}} 290 proj = proj.Init(ctx, stats, 0) 291 c.Assert(checkPhysicalCausetClone(proj), IsNil) 292 293 // limit 294 lim := &PhysicalLimit{Count: 1, Offset: 2} 295 lim = lim.Init(ctx, stats, 0) 296 c.Assert(checkPhysicalCausetClone(lim), IsNil) 297 298 // sort 299 byItems := []*soliton.ByItems{{Expr: col}, {Expr: cst}} 300 sort := &PhysicalSort{ByItems: byItems} 301 sort = sort.Init(ctx, stats, 0) 302 c.Assert(checkPhysicalCausetClone(sort), IsNil) 303 304 // topN 305 topN := &PhysicalTopN{ByItems: byItems, Offset: 2333, Count: 2333} 306 topN = topN.Init(ctx, stats, 0) 307 c.Assert(checkPhysicalCausetClone(topN), IsNil) 308 309 // stream agg 310 streamAgg := &PhysicalStreamAgg{basePhysicalAgg{ 311 AggFuncs: aggDescs, 312 GroupByItems: []memex.Expression{col, cst}, 313 }} 314 streamAgg = streamAgg.initForStream(ctx, stats, 0) 315 streamAgg.SetSchema(schemaReplicant) 316 c.Assert(checkPhysicalCausetClone(streamAgg), IsNil) 317 318 // hash agg 319 hashAgg := &PhysicalHashAgg{basePhysicalAgg{ 320 AggFuncs: aggDescs, 321 GroupByItems: []memex.Expression{col, cst}, 322 }} 323 hashAgg = hashAgg.initForHash(ctx, stats, 0) 324 hashAgg.SetSchema(schemaReplicant) 325 c.Assert(checkPhysicalCausetClone(hashAgg), IsNil) 326 327 // hash join 328 hashJoin := &PhysicalHashJoin{ 329 Concurrency: 4, 330 UseOuterToBuild: true, 331 } 332 hashJoin = hashJoin.Init(ctx, stats, 0) 333 hashJoin.SetSchema(schemaReplicant) 334 c.Assert(checkPhysicalCausetClone(hashJoin), IsNil) 335 336 // merge join 337 mergeJoin := &PhysicalMergeJoin{ 338 CompareFuncs: []memex.CompareFunc{memex.CompareInt}, 339 Desc: true, 340 } 341 mergeJoin = mergeJoin.Init(ctx, stats, 0) 342 mergeJoin.SetSchema(schemaReplicant) 343 c.Assert(checkPhysicalCausetClone(mergeJoin), IsNil) 344 } 345 346 //go:linkname valueInterface reflect.valueInterface 347 func valueInterface(v reflect.Value, safe bool) interface{} 348 349 func typeName(t reflect.Type) string { 350 path := t.String() 351 tmp := strings.Split(path, ".") 352 return tmp[len(tmp)-1] 353 } 354 355 func checkPhysicalCausetClone(p PhysicalCauset) error { 356 cloned, err := p.Clone() 357 if err != nil { 358 return err 359 } 360 whiteList := []string{"*property.StatsInfo", "*stochastikctx.Context", "*mock.Context", "*types.FieldType"} 361 return checkDeepClonedCore(reflect.ValueOf(p), reflect.ValueOf(cloned), typeName(reflect.TypeOf(p)), whiteList, nil) 362 } 363 364 // checkDeepClonedCore is used to check if v2 is deep cloned from v1. 365 // It's modified from reflect.deepValueEqual. We cannot use reflect.DeepEqual here since they have different 366 // logic, for example, if two pointers point the same address, they will pass the DeepEqual check while failing in the DeepClone check. 367 func checkDeepClonedCore(v1, v2 reflect.Value, path string, whiteList []string, visited map[visit]bool) error { 368 if !v1.IsValid() || !v2.IsValid() { 369 if v1.IsValid() != v2.IsValid() { 370 return errors.Errorf("invalid") 371 } 372 return nil 373 } 374 if v1.Type() != v2.Type() { 375 return errors.Errorf("different type %v, %v, path %v", v1.Type(), v2.Type(), path) 376 } 377 378 if visited == nil { 379 visited = make(map[visit]bool) 380 } 381 hard := func(k reflect.HoTT) bool { 382 switch k { 383 case reflect.Map, reflect.Slice, reflect.Ptr, reflect.Interface: 384 return true 385 } 386 return false 387 } 388 if v1.CanAddr() && v2.CanAddr() && hard(v1.HoTT()) { 389 addr1 := unsafe.Pointer(v1.UnsafeAddr()) 390 addr2 := unsafe.Pointer(v2.UnsafeAddr()) 391 if uintptr(addr1) > uintptr(addr2) { 392 addr1, addr2 = addr2, addr1 393 } 394 typ := v1.Type() 395 v := visit{addr1, addr2, typ} 396 if visited[v] { 397 return nil 398 } 399 visited[v] = true 400 } 401 402 switch v1.HoTT() { 403 case reflect.Array: 404 for i := 0; i < v1.Len(); i++ { 405 if err := checkDeepClonedCore(v1.Index(i), v2.Index(i), fmt.Sprintf("%v[%v]", path, i), whiteList, visited); err != nil { 406 return err 407 } 408 } 409 case reflect.Slice: 410 if (v1.IsNil() && v2.IsNil()) || (v1.Len() == 0 && v2.Len() == 0) { 411 return nil 412 } 413 if v1.Len() != v2.Len() { 414 return errors.Errorf("different slice lengths, len %v, %v, path %v", v1.Len(), v2.Len(), path) 415 } 416 if v1.IsNil() != v2.IsNil() { 417 if v1.Len() == 0 && v2.Len() == 0 { 418 return nil // nil and an empty slice are accepted 419 } 420 return errors.Errorf("different slices nil %v, %v, path %v", v1.IsNil(), v2.IsNil(), path) 421 } 422 if v1.Pointer() == v2.Pointer() { 423 return errors.Errorf("invalid slice pointers, path %v", path) 424 } 425 for i := 0; i < v1.Len(); i++ { 426 if err := checkDeepClonedCore(v1.Index(i), v2.Index(i), fmt.Sprintf("%v[%v]", path, i), whiteList, visited); err != nil { 427 return err 428 } 429 } 430 case reflect.Interface: 431 if v1.IsNil() && v2.IsNil() { 432 return nil 433 } 434 if v1.IsNil() != v2.IsNil() { 435 return errors.Errorf("invalid interfaces, path %v", path) 436 } 437 return checkDeepClonedCore(v1.Elem(), v2.Elem(), path, whiteList, visited) 438 case reflect.Ptr: 439 if v1.IsNil() && v2.IsNil() { 440 return nil 441 } 442 if v1.Pointer() == v2.Pointer() { 443 typeName := v1.Type().String() 444 inWhiteList := false 445 for _, whiteName := range whiteList { 446 if whiteName == typeName { 447 inWhiteList = true 448 break 449 } 450 } 451 if inWhiteList { 452 return nil 453 } 454 return errors.Errorf("same pointer, path %v", path) 455 } 456 return checkDeepClonedCore(v1.Elem(), v2.Elem(), path, whiteList, visited) 457 case reflect.Struct: 458 for i, n := 0, v1.NumField(); i < n; i++ { 459 if err := checkDeepClonedCore(v1.Field(i), v2.Field(i), fmt.Sprintf("%v.%v", path, typeName(v1.Field(i).Type())), whiteList, visited); err != nil { 460 return err 461 } 462 } 463 case reflect.Map: 464 if (v1.IsNil() && v2.IsNil()) || (v1.Len() == 0 && v2.Len() == 0) { 465 return nil 466 } 467 if v1.IsNil() != v2.IsNil() || v1.Len() != v2.Len() { 468 return errors.Errorf("different maps nil: %v, %v, len: %v, %v, path: %v", v1.IsNil(), v2.IsNil(), v1.Len(), v2.Len(), path) 469 } 470 if v1.Pointer() == v2.Pointer() { 471 return errors.Errorf("invalid map pointers, path %v", path) 472 } 473 if len(v1.MapKeys()) != len(v2.MapKeys()) { 474 return errors.Errorf("invalid map") 475 } 476 for _, k := range v1.MapKeys() { 477 val1 := v1.MapIndex(k) 478 val2 := v2.MapIndex(k) 479 if !val1.IsValid() || !val2.IsValid() { 480 if err := checkDeepClonedCore(val1, val2, fmt.Sprintf("%v[%v]", path, typeName(k.Type())), whiteList, visited); err != nil { 481 return err 482 } 483 } 484 } 485 case reflect.Func: 486 if v1.IsNil() != v2.IsNil() { 487 return errors.Errorf("invalid functions, path %v", path) 488 } 489 return nil // assume that these functions are stateless 490 default: 491 if valueInterface(v1, false) != valueInterface(v2, false) { 492 return errors.Errorf("different values, path %v", path) 493 } 494 } 495 return nil 496 } 497 498 type visit struct { 499 a1 unsafe.Pointer 500 a2 unsafe.Pointer 501 typ reflect.Type 502 }