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  }