github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/plan/apply_indices.go (about) 1 // Copyright 2023 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 plan 16 17 import ( 18 "fmt" 19 "sort" 20 21 "github.com/matrixorigin/matrixone/pkg/catalog" 22 23 "github.com/matrixorigin/matrixone/pkg/container/types" 24 "github.com/matrixorigin/matrixone/pkg/pb/plan" 25 ) 26 27 func isRuntimeConstExpr(expr *plan.Expr) bool { 28 switch expr.Expr.(type) { 29 case *plan.Expr_Lit, *plan.Expr_P, *plan.Expr_V: 30 return true 31 32 case *plan.Expr_F: 33 fn := expr.GetF() 34 return fn.Func.ObjName == "cast" && isRuntimeConstExpr(fn.Args[0]) 35 36 default: 37 return false 38 } 39 } 40 41 func (builder *QueryBuilder) applyIndices(nodeID int32, colRefCnt map[[2]int32]int, idxColMap map[[2]int32]*plan.Expr) int32 { 42 if builder.optimizerHints != nil && builder.optimizerHints.applyIndices != 0 { 43 return nodeID 44 } 45 node := builder.qry.Nodes[nodeID] 46 for i, childID := range node.Children { 47 node.Children[i] = builder.applyIndices(childID, colRefCnt, idxColMap) 48 } 49 replaceColumnsForNode(node, idxColMap) 50 51 switch node.NodeType { 52 case plan.Node_TABLE_SCAN: 53 return builder.applyIndicesForFilters(nodeID, node, colRefCnt, idxColMap) 54 55 case plan.Node_JOIN: 56 return builder.applyIndicesForJoins(nodeID, node, colRefCnt, idxColMap) 57 58 case plan.Node_PROJECT: 59 //NOTE: This is the entry point for vector index rule on SORT NODE. 60 return builder.applyIndicesForProject(nodeID, node, colRefCnt, idxColMap) 61 62 } 63 return nodeID 64 } 65 66 func (builder *QueryBuilder) applyIndicesForFilters(nodeID int32, node *plan.Node, 67 colRefCnt map[[2]int32]int, idxColMap map[[2]int32]*plan.Expr) int32 { 68 69 if len(node.FilterList) == 0 || len(node.TableDef.Indexes) == 0 { 70 return nodeID 71 } 72 // 1. Master Index Check 73 { 74 masterIndexes := make([]*plan.IndexDef, 0) 75 for _, indexDef := range node.TableDef.Indexes { 76 if !indexDef.Unique && catalog.IsMasterIndexAlgo(indexDef.IndexAlgo) { 77 masterIndexes = append(masterIndexes, indexDef) 78 } 79 } 80 81 if len(masterIndexes) == 0 { 82 goto END0 83 } 84 85 for _, expr := range node.FilterList { 86 fn := expr.GetF() 87 if fn == nil { 88 goto END0 89 } 90 91 switch fn.Func.ObjName { 92 case "=": 93 if isRuntimeConstExpr(fn.Args[0]) && fn.Args[1].GetCol() != nil { 94 fn.Args[0], fn.Args[1] = fn.Args[1], fn.Args[0] 95 } 96 97 if !isRuntimeConstExpr(fn.Args[1]) { 98 goto END0 99 } 100 case "between": 101 case "in": 102 103 default: 104 goto END0 105 } 106 107 col := fn.Args[0].GetCol() 108 if col == nil { 109 goto END0 110 } 111 } 112 113 for _, indexDef := range masterIndexes { 114 isAllFilterColumnsIncluded := true 115 for _, expr := range node.FilterList { 116 fn := expr.GetF() 117 col := fn.Args[0].GetCol() 118 if !isKeyPresentInList(col.Name, indexDef.Parts) { 119 isAllFilterColumnsIncluded = false 120 break 121 } 122 } 123 if isAllFilterColumnsIncluded { 124 return builder.applyIndicesForFiltersUsingMasterIndex(nodeID, node, indexDef) 125 } 126 } 127 128 } 129 END0: 130 // 2. Regular Index Check 131 { 132 return builder.applyIndicesForFiltersRegularIndex(nodeID, node, colRefCnt, idxColMap) 133 } 134 } 135 136 func getColSeqFromColDef(tblCol *plan.ColDef) string { 137 return fmt.Sprintf("%d", tblCol.GetSeqnum()) 138 } 139 140 func (builder *QueryBuilder) applyIndicesForProject(nodeID int32, projNode *plan.Node, colRefCnt map[[2]int32]int, idxColMap map[[2]int32]*plan.Expr) int32 { 141 142 // 1. Vector Index Check 143 // Handle Queries like 144 // SELECT id,embedding FROM tbl ORDER BY l2_distance(embedding, "[1,2,3]") LIMIT 10; 145 { 146 sortNode := builder.resolveSortNode(projNode, 1) 147 if sortNode == nil || len(sortNode.OrderBy) != 1 { 148 goto END0 149 } 150 151 scanNode := builder.resolveScanNodeWithIndex(sortNode, 1) 152 if scanNode == nil { 153 goto END0 154 } 155 156 // 1.a if there are no table scans with multi-table indexes, skip 157 multiTableIndexes := make(map[string]*MultiTableIndex) 158 for _, indexDef := range scanNode.TableDef.Indexes { 159 if catalog.IsIvfIndexAlgo(indexDef.IndexAlgo) { 160 if _, ok := multiTableIndexes[indexDef.IndexName]; !ok { 161 multiTableIndexes[indexDef.IndexName] = &MultiTableIndex{ 162 IndexAlgo: catalog.ToLower(indexDef.IndexAlgo), 163 IndexDefs: make(map[string]*plan.IndexDef), 164 } 165 } 166 multiTableIndexes[indexDef.IndexName].IndexDefs[catalog.ToLower(indexDef.IndexAlgoTableType)] = indexDef 167 } 168 } 169 if len(multiTableIndexes) == 0 { 170 return nodeID 171 } 172 173 //1.b if sortNode has more than one order by, skip 174 if len(sortNode.OrderBy) != 1 { 175 goto END0 176 } 177 178 // 1.c if sortNode does not have a registered distance function, skip 179 distFnExpr := sortNode.OrderBy[0].Expr.GetF() 180 if distFnExpr == nil { 181 goto END0 182 } 183 if _, ok := distFuncOpTypes[distFnExpr.Func.ObjName]; !ok { 184 goto END0 185 } 186 187 // 1.d if the order by argument order is not of the form dist_func(col, const), swap and see 188 // if that works. if not, skip 189 if isRuntimeConstExpr(distFnExpr.Args[0]) && distFnExpr.Args[1].GetCol() != nil { 190 distFnExpr.Args[0], distFnExpr.Args[1] = distFnExpr.Args[1], distFnExpr.Args[0] 191 } 192 if !isRuntimeConstExpr(distFnExpr.Args[1]) { 193 goto END0 194 } 195 if distFnExpr.Args[0].GetCol() == nil { 196 goto END0 197 } 198 // NOTE: here we assume the first argument is the column to order by 199 colPosOrderBy := distFnExpr.Args[0].GetCol().ColPos 200 201 // 1.d if the distance function in sortNode is not indexed for that column in any of the IVFFLAT index, skip 202 distanceFunctionIndexed := false 203 var multiTableIndexWithSortDistFn *MultiTableIndex 204 205 // This is important to get consistent result. 206 // HashMap can give you random order during iteration. 207 var multiTableIndexKeys []string 208 for key := range multiTableIndexes { 209 multiTableIndexKeys = append(multiTableIndexKeys, key) 210 } 211 sort.Strings(multiTableIndexKeys) 212 213 for _, multiTableIndexKey := range multiTableIndexKeys { 214 multiTableIndex := multiTableIndexes[multiTableIndexKey] 215 switch multiTableIndex.IndexAlgo { 216 case catalog.MoIndexIvfFlatAlgo.ToString(): 217 storedParams, err := catalog.IndexParamsStringToMap(multiTableIndex.IndexDefs[catalog.SystemSI_IVFFLAT_TblType_Metadata].IndexAlgoParams) 218 if err != nil { 219 continue 220 } 221 storedOpType, ok := storedParams[catalog.IndexAlgoParamOpType] 222 if !ok { 223 continue 224 } 225 226 // if index is not the order by column, skip 227 idxDef0 := multiTableIndex.IndexDefs[catalog.SystemSI_IVFFLAT_TblType_Metadata] 228 if scanNode.TableDef.Name2ColIndex[idxDef0.Parts[0]] != colPosOrderBy { 229 continue 230 } 231 232 // if index is of the same distance function in order by, the index is valid 233 if storedOpType == distFuncOpTypes[distFnExpr.Func.ObjName] { 234 distanceFunctionIndexed = true 235 multiTableIndexWithSortDistFn = multiTableIndex 236 } 237 } 238 if distanceFunctionIndexed { 239 break 240 } 241 } 242 if !distanceFunctionIndexed { 243 goto END0 244 } 245 246 newSortNode := builder.applyIndicesForSortUsingVectorIndex(nodeID, projNode, sortNode, scanNode, 247 colRefCnt, idxColMap, multiTableIndexWithSortDistFn, colPosOrderBy) 248 249 // TODO: consult with nitao and aungr 250 projNode.Children[0] = newSortNode 251 replaceColumnsForNode(projNode, idxColMap) 252 253 return newSortNode 254 } 255 END0: 256 // 2. Regular Index Check 257 { 258 259 } 260 261 return nodeID 262 } 263 264 func (builder *QueryBuilder) applyIndicesForFiltersRegularIndex(nodeID int32, node *plan.Node, colRefCnt map[[2]int32]int, idxColMap map[[2]int32]*plan.Expr) int32 { 265 if len(node.FilterList) == 0 || len(node.TableDef.Indexes) == 0 { 266 return nodeID 267 } 268 269 //---------------------------------------------------------------------- 270 //ts1 := node.GetScanTS() 271 272 scanSnapshot := node.ScanSnapshot 273 if scanSnapshot == nil { 274 scanSnapshot = &Snapshot{} 275 } 276 //---------------------------------------------------------------------- 277 278 var pkPos int32 = -1 279 if len(node.TableDef.Pkey.Names) == 1 { 280 pkPos = node.TableDef.Name2ColIndex[node.TableDef.Pkey.Names[0]] 281 } 282 283 indexes := node.TableDef.Indexes 284 sort.Slice(indexes, func(i, j int) bool { 285 return (indexes[i].Unique && !indexes[j].Unique) || (indexes[i].Unique == indexes[j].Unique && len(indexes[i].Parts) > len(indexes[j].Parts)) 286 }) 287 288 // Apply unique/secondary indices if only indexed column is referenced 289 290 { 291 col2filter := make(map[int32]int) 292 colPos := int32(-1) 293 for i, expr := range node.FilterList { 294 fn := expr.GetF() 295 if fn == nil { 296 goto END0 297 } 298 299 switch fn.Func.ObjName { 300 case "=": 301 if isRuntimeConstExpr(fn.Args[0]) && fn.Args[1].GetCol() != nil { 302 fn.Args[0], fn.Args[1] = fn.Args[1], fn.Args[0] 303 } 304 305 col := fn.Args[0].GetCol() 306 if col == nil || colPos != -1 || !isRuntimeConstExpr(fn.Args[1]) { 307 goto END0 308 } 309 310 col2filter[col.ColPos] = i 311 312 case "in", "between": 313 col := fn.Args[0].GetCol() 314 if col == nil { 315 goto END0 316 } 317 318 if len(col2filter) > 0 || (colPos != -1 && colPos != col.ColPos) { 319 goto END0 320 } 321 322 colPos = col.ColPos 323 324 default: 325 goto END0 326 } 327 } 328 329 if colPos == pkPos { 330 return nodeID 331 } 332 333 if colPos > -1 { 334 for i := range node.TableDef.Cols { 335 if i != int(colPos) && colRefCnt[[2]int32{node.BindingTags[0], int32(i)}] > 0 { 336 goto END0 337 } 338 } 339 } 340 341 hitFilterIdx := make([]int, 0, len(col2filter)) 342 missFilterIdx := make([]int, 0, len(node.FilterList)) 343 for _, idxDef := range indexes { 344 if !idxDef.TableExist { 345 continue 346 } 347 348 numParts := len(idxDef.Parts) 349 if idxDef.Unique { 350 if len(col2filter) > 0 && len(col2filter) < numParts { 351 continue 352 } 353 if colPos > -1 && numParts > 1 { 354 continue 355 } 356 } 357 358 numKeyParts := numParts 359 if !idxDef.Unique { 360 numKeyParts-- 361 } 362 if numKeyParts == 0 { 363 continue 364 } 365 366 if colPos != -1 { 367 if node.TableDef.Name2ColIndex[idxDef.Parts[0]] != colPos { 368 continue 369 } 370 } else { 371 hitFilterIdx = hitFilterIdx[:0] 372 missFilterIdx = missFilterIdx[:0] 373 hitPrefix := true 374 indexedCols := make(map[int32]bool) 375 for i := 0; i < numKeyParts; i++ { 376 colIdx := node.TableDef.Name2ColIndex[idxDef.Parts[i]] 377 idx, ok := col2filter[colIdx] 378 if ok { 379 if hitPrefix { 380 hitFilterIdx = append(hitFilterIdx, idx) 381 } else { 382 missFilterIdx = append(missFilterIdx, idx) 383 } 384 } else { 385 hitPrefix = false 386 } 387 indexedCols[colIdx] = true 388 } 389 390 for i := range node.TableDef.Cols { 391 if !indexedCols[int32(i)] && colRefCnt[[2]int32{node.BindingTags[0], int32(i)}] > 0 { 392 hitFilterIdx = hitFilterIdx[:0] 393 break 394 } 395 } 396 397 if len(hitFilterIdx) == 0 || len(hitFilterIdx)+len(missFilterIdx) < len(node.FilterList) { 398 continue 399 } 400 } 401 402 idxTag := builder.genNewTag() 403 404 //idxObjRef, idxTableDef := builder.compCtx.Resolve(node.ObjRef.SchemaName, idxDef.IndexTableName, *ts) 405 idxObjRef, idxTableDef := builder.compCtx.Resolve(node.ObjRef.SchemaName, idxDef.IndexTableName, *scanSnapshot) 406 407 builder.addNameByColRef(idxTag, idxTableDef) 408 409 idxColExpr := &plan.Expr{ 410 Typ: idxTableDef.Cols[0].Typ, 411 Expr: &plan.Expr_Col{ 412 Col: &plan.ColRef{ 413 RelPos: idxTag, 414 ColPos: 0, 415 }, 416 }, 417 } 418 419 if colPos != -1 { // a IN (1, 2, 3), a BETWEEN 1 AND 2 420 if numParts > 1 { 421 origType := node.TableDef.Cols[colPos].Typ 422 idxColExpr, _ = BindFuncExprImplByPlanExpr(builder.GetContext(), "serial_extract", []*plan.Expr{ 423 idxColExpr, 424 { 425 Typ: plan.Type{ 426 Id: int32(types.T_int64), 427 }, 428 Expr: &plan.Expr_Lit{ 429 Lit: &plan.Literal{ 430 Value: &plan.Literal_I64Val{I64Val: 0}, 431 }, 432 }, 433 }, 434 { 435 Typ: origType, 436 Expr: &plan.Expr_T{ 437 T: &plan.TargetType{}, 438 }, 439 }, 440 }) 441 } 442 443 idxColMap[[2]int32{node.BindingTags[0], colPos}] = idxColExpr 444 445 for i, expr := range node.FilterList { 446 fn := expr.GetF() 447 col := fn.Args[0].GetCol() 448 col.RelPos = idxTag 449 col.ColPos = 0 450 451 if !idxDef.Unique { 452 fn.Args[0].Typ = idxTableDef.Cols[0].Typ 453 switch fn.Func.ObjName { 454 case "between": 455 fn.Args[1], _ = BindFuncExprImplByPlanExpr(builder.GetContext(), "serial", []*plan.Expr{fn.Args[1]}) 456 fn.Args[2], _ = BindFuncExprImplByPlanExpr(builder.GetContext(), "serial", []*plan.Expr{fn.Args[2]}) 457 node.FilterList[i], _ = bindFuncExprAndConstFold(builder.GetContext(), builder.compCtx.GetProcess(), "prefix_between", fn.Args) 458 459 case "in": 460 fn.Args[1], _ = BindFuncExprImplByPlanExpr(builder.GetContext(), "serial", []*plan.Expr{fn.Args[1]}) 461 node.FilterList[i], _ = bindFuncExprAndConstFold(builder.GetContext(), builder.compCtx.GetProcess(), "prefix_in", fn.Args) 462 } 463 } 464 } 465 } else { // a = 1 AND b = 2 AND c = 3 466 if numParts == 1 { 467 idx := hitFilterIdx[0] 468 idxFilter := node.FilterList[idx] 469 args := idxFilter.GetF().Args 470 col := args[0].GetCol() 471 col.RelPos = idxTag 472 oldColPos := col.ColPos 473 col.ColPos = 0 474 475 node.FilterList[idx] = idxFilter 476 idxColMap[[2]int32{node.BindingTags[0], oldColPos}] = idxColExpr 477 } else { 478 for i := 0; i < numKeyParts; i++ { 479 colIdx := node.TableDef.Name2ColIndex[idxDef.Parts[i]] 480 origType := node.TableDef.Cols[colIdx].Typ 481 mappedExpr, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "serial_extract", []*plan.Expr{ 482 DeepCopyExpr(idxColExpr), 483 { 484 Typ: plan.Type{ 485 Id: int32(types.T_int64), 486 }, 487 Expr: &plan.Expr_Lit{ 488 Lit: &plan.Literal{ 489 Value: &plan.Literal_I64Val{I64Val: int64(i)}, 490 }, 491 }, 492 }, 493 { 494 Typ: origType, 495 Expr: &plan.Expr_T{ 496 T: &plan.TargetType{}, 497 }, 498 }, 499 }) 500 501 idxColMap[[2]int32{node.BindingTags[0], colIdx}] = mappedExpr 502 } 503 504 serialArgs := make([]*plan.Expr, len(hitFilterIdx)) 505 for i := range hitFilterIdx { 506 serialArgs[i] = DeepCopyExpr(node.FilterList[hitFilterIdx[i]].GetF().Args[1]) 507 } 508 rightArg, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "serial", serialArgs) 509 510 funcName := "=" 511 if len(hitFilterIdx) < numParts { 512 funcName = "prefix_eq" 513 } 514 idxFilter, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), funcName, []*plan.Expr{ 515 { 516 Typ: idxTableDef.Cols[0].Typ, 517 Expr: &plan.Expr_Col{ 518 Col: &plan.ColRef{ 519 RelPos: idxTag, 520 ColPos: 0, 521 }, 522 }, 523 }, 524 rightArg, 525 }) 526 527 newFilterList := make([]*plan.Expr, 0, len(missFilterIdx)+1) 528 for _, idx := range missFilterIdx { 529 newFilterList = append(newFilterList, replaceColumnsForExpr(node.FilterList[idx], idxColMap)) 530 } 531 node.FilterList = append(newFilterList, idxFilter) 532 } 533 } 534 535 idxTableNodeID := builder.appendNode(&plan.Node{ 536 NodeType: plan.Node_TABLE_SCAN, 537 TableDef: idxTableDef, 538 ObjRef: idxObjRef, 539 ParentObjRef: node.ObjRef, 540 FilterList: node.FilterList, 541 Limit: node.Limit, 542 Offset: node.Offset, 543 BindingTags: []int32{idxTag}, 544 ScanSnapshot: node.ScanSnapshot, 545 }, builder.ctxByNode[nodeID]) 546 547 return idxTableNodeID 548 } 549 550 } 551 552 END0: 553 if node.Stats.Selectivity > InFilterSelectivityLimit || node.Stats.Outcnt > float64(GetInFilterCardLimitOnPK(node.Stats.TableCnt)) { 554 return nodeID 555 } 556 557 // Apply unique/secondary indices for point select 558 559 col2filter := make(map[int32]int) 560 for i, expr := range node.FilterList { 561 fn := expr.GetF() 562 if fn == nil { 563 continue 564 } 565 566 if fn.Func.ObjName != "=" { 567 continue 568 } 569 570 if isRuntimeConstExpr(fn.Args[0]) && fn.Args[1].GetCol() != nil { 571 fn.Args[0], fn.Args[1] = fn.Args[1], fn.Args[0] 572 } 573 574 col := fn.Args[0].GetCol() 575 if col == nil || !isRuntimeConstExpr(fn.Args[1]) { 576 continue 577 } 578 579 col2filter[col.ColPos] = i 580 } 581 582 filterOnPK := true 583 for _, part := range node.TableDef.Pkey.Names { 584 colIdx := node.TableDef.Name2ColIndex[part] 585 _, ok := col2filter[colIdx] 586 if !ok { 587 filterOnPK = false 588 break 589 } 590 } 591 592 if filterOnPK { 593 return nodeID 594 } 595 596 filterIdx := make([]int, 0, len(col2filter)) 597 for _, idxDef := range indexes { 598 if !idxDef.TableExist { 599 continue 600 } 601 602 numParts := len(idxDef.Parts) 603 numKeyParts := numParts 604 if !idxDef.Unique { 605 numKeyParts-- 606 } 607 if numKeyParts == 0 { 608 continue 609 } 610 611 usePartialIndex := false 612 613 filterIdx = filterIdx[:0] 614 for i := 0; i < numKeyParts; i++ { 615 colIdx := node.TableDef.Name2ColIndex[idxDef.Parts[i]] 616 idx, ok := col2filter[colIdx] 617 if !ok { 618 break 619 } 620 621 filterIdx = append(filterIdx, idx) 622 623 filter := node.FilterList[idx] 624 if filter.Selectivity <= InFilterSelectivityLimit && node.Stats.TableCnt*filter.Selectivity <= float64(GetInFilterCardLimitOnPK(node.Stats.TableCnt)) { 625 usePartialIndex = true 626 } 627 } 628 629 if len(filterIdx) < numParts && (idxDef.Unique || !usePartialIndex) { 630 continue 631 } 632 633 idxTag := builder.genNewTag() 634 635 //idxObjRef, idxTableDef := builder.compCtx.Resolve(node.ObjRef.SchemaName, idxDef.IndexTableName, *ts) 636 idxObjRef, idxTableDef := builder.compCtx.Resolve(node.ObjRef.SchemaName, idxDef.IndexTableName, *scanSnapshot) 637 builder.addNameByColRef(idxTag, idxTableDef) 638 639 var idxFilter *plan.Expr 640 if numParts == 1 { 641 idx := filterIdx[0] 642 idxFilter = DeepCopyExpr(node.FilterList[idx]) 643 args := idxFilter.GetF().Args 644 col := args[0].GetCol() 645 col.RelPos = idxTag 646 col.ColPos = 0 647 } else { 648 serialArgs := make([]*plan.Expr, len(filterIdx)) 649 for i := range filterIdx { 650 serialArgs[i] = DeepCopyExpr(node.FilterList[filterIdx[i]].GetF().Args[1]) 651 } 652 rightArg, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "serial", serialArgs) 653 654 funcName := "=" 655 if len(filterIdx) < numParts { 656 funcName = "prefix_eq" 657 } 658 idxFilter, _ = BindFuncExprImplByPlanExpr(builder.GetContext(), funcName, []*plan.Expr{ 659 { 660 Typ: idxTableDef.Cols[0].Typ, 661 Expr: &plan.Expr_Col{ 662 Col: &plan.ColRef{ 663 RelPos: idxTag, 664 ColPos: 0, 665 }, 666 }, 667 }, 668 rightArg, 669 }) 670 } 671 672 idxTableNodeID := builder.appendNode(&plan.Node{ 673 NodeType: plan.Node_TABLE_SCAN, 674 TableDef: idxTableDef, 675 ObjRef: idxObjRef, 676 ParentObjRef: DeepCopyObjectRef(node.ObjRef), 677 FilterList: []*plan.Expr{idxFilter}, 678 Limit: node.Limit, 679 Offset: node.Offset, 680 BindingTags: []int32{idxTag}, 681 ScanSnapshot: node.ScanSnapshot, 682 }, builder.ctxByNode[nodeID]) 683 684 node.Limit, node.Offset = nil, nil 685 686 pkIdx := node.TableDef.Name2ColIndex[node.TableDef.Pkey.PkeyColName] 687 pkExpr := &plan.Expr{ 688 Typ: node.TableDef.Cols[pkIdx].Typ, 689 Expr: &plan.Expr_Col{ 690 Col: &plan.ColRef{ 691 RelPos: node.BindingTags[0], 692 ColPos: pkIdx, 693 }, 694 }, 695 } 696 697 joinCond, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*plan.Expr{ 698 pkExpr, 699 { 700 Typ: pkExpr.Typ, 701 Expr: &plan.Expr_Col{ 702 Col: &plan.ColRef{ 703 RelPos: idxTag, 704 ColPos: 1, 705 }, 706 }, 707 }, 708 }) 709 joinNodeID := builder.appendNode(&plan.Node{ 710 NodeType: plan.Node_JOIN, 711 Children: []int32{nodeID, idxTableNodeID}, 712 JoinType: plan.Node_INDEX, 713 OnList: []*plan.Expr{joinCond}, 714 }, builder.ctxByNode[nodeID]) 715 716 return joinNodeID 717 } 718 719 // Apply single-column unique/secondary indices for non-equi expression 720 721 colPos2Idx := make(map[int32]int) 722 723 for i, idxDef := range indexes { 724 if !idxDef.TableExist { 725 continue 726 } 727 728 numParts := len(idxDef.Parts) 729 if !idxDef.Unique { 730 numParts-- 731 } 732 733 if numParts == 1 { 734 colPos2Idx[node.TableDef.Name2ColIndex[idxDef.Parts[0]]] = i 735 } 736 } 737 738 for i := range node.FilterList { 739 expr := DeepCopyExpr(node.FilterList[i]) 740 fn := expr.GetF() 741 if fn == nil { 742 continue 743 } 744 745 col := fn.Args[0].GetCol() 746 if col == nil { 747 continue 748 } 749 750 if col.ColPos == pkPos { 751 return nodeID 752 } 753 754 switch fn.Func.ObjName { 755 case "between", "in": 756 757 default: 758 continue 759 } 760 761 idxPos, ok := colPos2Idx[col.ColPos] 762 if !ok { 763 continue 764 } 765 766 idxTag := builder.genNewTag() 767 idxDef := node.TableDef.Indexes[idxPos] 768 //idxObjRef, idxTableDef := builder.compCtx.Resolve(node.ObjRef.SchemaName, idxDef.IndexTableName, *ts) 769 idxObjRef, idxTableDef := builder.compCtx.Resolve(node.ObjRef.SchemaName, idxDef.IndexTableName, *scanSnapshot) 770 builder.addNameByColRef(idxTag, idxTableDef) 771 772 col.RelPos = idxTag 773 col.ColPos = 0 774 775 var idxFilter *plan.Expr 776 if idxDef.Unique { 777 idxFilter = expr 778 } else { 779 fn.Args[0].Typ = idxTableDef.Cols[0].Typ 780 781 switch fn.Func.ObjName { 782 case "in": 783 fn.Args[1], _ = BindFuncExprImplByPlanExpr(builder.GetContext(), "serial", []*plan.Expr{fn.Args[1]}) 784 idxFilter, _ = bindFuncExprAndConstFold(builder.GetContext(), builder.compCtx.GetProcess(), "prefix_in", fn.Args) 785 786 case "between": 787 fn.Args[1], _ = BindFuncExprImplByPlanExpr(builder.GetContext(), "serial", []*plan.Expr{fn.Args[1]}) 788 fn.Args[2], _ = BindFuncExprImplByPlanExpr(builder.GetContext(), "serial", []*plan.Expr{fn.Args[2]}) 789 idxFilter, _ = bindFuncExprAndConstFold(builder.GetContext(), builder.compCtx.GetProcess(), "prefix_between", fn.Args) 790 } 791 } 792 793 idxTableNodeID := builder.appendNode(&plan.Node{ 794 NodeType: plan.Node_TABLE_SCAN, 795 TableDef: idxTableDef, 796 ObjRef: idxObjRef, 797 ParentObjRef: DeepCopyObjectRef(node.ObjRef), 798 FilterList: []*plan.Expr{idxFilter}, 799 Limit: node.Limit, 800 Offset: node.Offset, 801 BindingTags: []int32{idxTag}, 802 ScanSnapshot: node.ScanSnapshot, 803 }, builder.ctxByNode[nodeID]) 804 805 node.Limit, node.Offset = nil, nil 806 807 pkIdx := node.TableDef.Name2ColIndex[node.TableDef.Pkey.PkeyColName] 808 pkExpr := &plan.Expr{ 809 Typ: node.TableDef.Cols[pkIdx].Typ, 810 Expr: &plan.Expr_Col{ 811 Col: &plan.ColRef{ 812 RelPos: node.BindingTags[0], 813 ColPos: pkIdx, 814 }, 815 }, 816 } 817 818 joinCond, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*plan.Expr{ 819 DeepCopyExpr(pkExpr), 820 { 821 Typ: pkExpr.Typ, 822 Expr: &plan.Expr_Col{ 823 Col: &plan.ColRef{ 824 RelPos: idxTag, 825 ColPos: 1, 826 }, 827 }, 828 }, 829 }) 830 joinNodeID := builder.appendNode(&plan.Node{ 831 NodeType: plan.Node_JOIN, 832 Children: []int32{nodeID, idxTableNodeID}, 833 JoinType: plan.Node_INDEX, 834 OnList: []*plan.Expr{joinCond}, 835 }, builder.ctxByNode[nodeID]) 836 837 return joinNodeID 838 } 839 840 return nodeID 841 } 842 843 func (builder *QueryBuilder) applyIndicesForJoins(nodeID int32, node *plan.Node, colRefCnt map[[2]int32]int, idxColMap map[[2]int32]*plan.Expr) int32 { 844 if node.JoinType == plan.Node_INDEX { 845 return nodeID 846 } 847 848 leftChild := builder.qry.Nodes[node.Children[0]] 849 if leftChild.NodeType != plan.Node_TABLE_SCAN { 850 return nodeID 851 } 852 853 //---------------------------------------------------------------------- 854 //ts2 := leftChild.GetScanTS() 855 856 scanSnapshot := leftChild.ScanSnapshot 857 if scanSnapshot == nil { 858 scanSnapshot = &Snapshot{} 859 } 860 //---------------------------------------------------------------------- 861 862 rightChild := builder.qry.Nodes[node.Children[1]] 863 864 if rightChild.Stats.Outcnt > float64(GetInFilterCardLimitOnPK(leftChild.Stats.TableCnt)) || rightChild.Stats.Outcnt > leftChild.Stats.Cost*0.1 { 865 return nodeID 866 } 867 868 leftTags := make(map[int32]bool) 869 for _, tag := range builder.enumerateTags(node.Children[0]) { 870 leftTags[tag] = true 871 } 872 873 rightTags := make(map[int32]bool) 874 for _, tag := range builder.enumerateTags(node.Children[1]) { 875 rightTags[tag] = true 876 } 877 878 col2Cond := make(map[int32]int) 879 for i, expr := range node.OnList { 880 if !isEquiCond(expr, leftTags, rightTags) { 881 continue 882 } 883 884 col := expr.GetF().Args[0].GetCol() 885 if col == nil { 886 continue 887 } 888 889 col2Cond[col.ColPos] = i 890 } 891 892 joinOnPK := true 893 for _, part := range leftChild.TableDef.Pkey.Names { 894 colIdx := leftChild.TableDef.Name2ColIndex[part] 895 _, ok := col2Cond[colIdx] 896 if !ok { 897 joinOnPK = false 898 break 899 } 900 } 901 902 if joinOnPK { 903 return nodeID 904 } 905 906 indexes := leftChild.TableDef.Indexes 907 condIdx := make([]int, 0, len(col2Cond)) 908 for _, idxDef := range indexes { 909 if !idxDef.TableExist { 910 continue 911 } 912 913 numParts := len(idxDef.Parts) 914 numKeyParts := numParts 915 if !idxDef.Unique { 916 numKeyParts-- 917 } 918 if numKeyParts == 0 || numKeyParts > len(col2Cond) { 919 continue 920 } 921 922 condIdx = condIdx[:0] 923 for i := 0; i < numKeyParts; i++ { 924 colIdx := leftChild.TableDef.Name2ColIndex[idxDef.Parts[i]] 925 idx, ok := col2Cond[colIdx] 926 if !ok { 927 break 928 } 929 930 condIdx = append(condIdx, idx) 931 } 932 933 if len(condIdx) < numKeyParts { 934 continue 935 } 936 937 idxTag := builder.genNewTag() 938 //idxObjRef, idxTableDef := builder.compCtx.Resolve(leftChild.ObjRef.SchemaName, idxDef.IndexTableName, *ts) 939 idxObjRef, idxTableDef := builder.compCtx.Resolve(leftChild.ObjRef.SchemaName, idxDef.IndexTableName, *scanSnapshot) 940 builder.addNameByColRef(idxTag, idxTableDef) 941 942 rfTag := builder.genNewMsgTag() 943 944 var rfBuildExpr *plan.Expr 945 if numParts == 1 { 946 rfBuildExpr = &plan.Expr{ 947 Typ: idxTableDef.Cols[0].Typ, 948 Expr: &plan.Expr_Col{ 949 Col: &plan.ColRef{ 950 RelPos: -1, 951 ColPos: 0, 952 }, 953 }, 954 } 955 } else { 956 serialArgs := make([]*plan.Expr, len(condIdx)) 957 for i := range condIdx { 958 serialArgs[i] = &plan.Expr{ 959 Typ: node.OnList[condIdx[i]].GetF().Args[1].Typ, 960 Expr: &plan.Expr_Col{ 961 Col: &plan.ColRef{ 962 RelPos: -1, 963 ColPos: int32(condIdx[i]), 964 }, 965 }, 966 } 967 } 968 rfBuildExpr, _ = BindFuncExprImplByPlanExpr(builder.GetContext(), "serial", serialArgs) 969 } 970 971 probeExpr := &plan.Expr{ 972 Typ: idxTableDef.Cols[0].Typ, 973 Expr: &plan.Expr_Col{ 974 Col: &plan.ColRef{ 975 RelPos: idxTag, 976 ColPos: 0, 977 }, 978 }, 979 } 980 idxTableNodeID := builder.appendNode(&plan.Node{ 981 NodeType: plan.Node_TABLE_SCAN, 982 TableDef: idxTableDef, 983 ObjRef: idxObjRef, 984 ParentObjRef: DeepCopyObjectRef(leftChild.ObjRef), 985 BindingTags: []int32{idxTag}, 986 ScanSnapshot: leftChild.ScanSnapshot, 987 RuntimeFilterProbeList: []*plan.RuntimeFilterSpec{MakeRuntimeFilter(rfTag, len(condIdx) < numParts, 0, probeExpr)}, 988 }, builder.ctxByNode[nodeID]) 989 990 node.RuntimeFilterBuildList = append(node.RuntimeFilterBuildList, MakeRuntimeFilter(rfTag, len(condIdx) < numParts, GetInFilterCardLimitOnPK(leftChild.Stats.TableCnt), rfBuildExpr)) 991 992 pkIdx := leftChild.TableDef.Name2ColIndex[leftChild.TableDef.Pkey.PkeyColName] 993 pkExpr := &plan.Expr{ 994 Typ: leftChild.TableDef.Cols[pkIdx].Typ, 995 Expr: &plan.Expr_Col{ 996 Col: &plan.ColRef{ 997 RelPos: leftChild.BindingTags[0], 998 ColPos: pkIdx, 999 }, 1000 }, 1001 } 1002 pkJoinCond, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*plan.Expr{ 1003 pkExpr, 1004 { 1005 Typ: pkExpr.Typ, 1006 Expr: &plan.Expr_Col{ 1007 Col: &plan.ColRef{ 1008 RelPos: idxTag, 1009 ColPos: 1, 1010 }, 1011 }, 1012 }, 1013 }) 1014 1015 idxJoinNodeID := builder.appendNode(&plan.Node{ 1016 NodeType: plan.Node_JOIN, 1017 Children: []int32{node.Children[0], idxTableNodeID}, 1018 JoinType: plan.Node_INDEX, 1019 Limit: leftChild.Limit, 1020 Offset: leftChild.Offset, 1021 OnList: []*plan.Expr{pkJoinCond}, 1022 }, builder.ctxByNode[nodeID]) 1023 1024 leftChild.Limit, leftChild.Offset = nil, nil 1025 1026 node.Children[0] = idxJoinNodeID 1027 1028 break 1029 } 1030 1031 return nodeID 1032 }