github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/plan/apply_indices_master.go (about)

     1  // Copyright 2024 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  	"github.com/matrixorigin/matrixone/pkg/common/mpool"
    19  	"github.com/matrixorigin/matrixone/pkg/container/types"
    20  	"github.com/matrixorigin/matrixone/pkg/container/vector"
    21  	"github.com/matrixorigin/matrixone/pkg/pb/plan"
    22  	"github.com/matrixorigin/matrixone/pkg/pb/timestamp"
    23  	"github.com/matrixorigin/matrixone/pkg/sql/plan/function"
    24  )
    25  
    26  var (
    27  	varcharType = types.T_varchar.ToType()
    28  )
    29  
    30  func (builder *QueryBuilder) applyIndicesForFiltersUsingMasterIndex(nodeID int32, scanNode *plan.Node, indexDef *plan.IndexDef) int32 {
    31  
    32  	var pkPos = scanNode.TableDef.Name2ColIndex[scanNode.TableDef.Pkey.PkeyColName]
    33  	var pkType = scanNode.TableDef.Cols[pkPos].Typ
    34  	var colDefs = scanNode.TableDef.Cols
    35  
    36  	var prevIndexPkCol *Expr
    37  	var prevLastNodeId int32
    38  	var lastNodeId int32
    39  
    40  	//ts1 := scanNode.ScanTS
    41  	for i, filterExp := range scanNode.FilterList {
    42  		// TODO: node should hold snapshot info and account info
    43  		//idxObjRef, idxTableDef := builder.compCtx.Resolve(scanNode.ObjRef.SchemaName, indexDef.IndexTableName, timestamp.Timestamp{})
    44  		idxObjRef, idxTableDef := builder.compCtx.Resolve(scanNode.ObjRef.SchemaName, indexDef.IndexTableName, Snapshot{TS: &timestamp.Timestamp{}})
    45  
    46  		// 1. SELECT pk from idx WHERE prefix_eq(`__mo_index_idx_col`,serial_full("0","value"))
    47  		currIdxProjTag, currScanId := makeIndexTblScan(builder, builder.ctxByNode[nodeID], filterExp, idxTableDef, idxObjRef, scanNode.ScanSnapshot, colDefs)
    48  
    49  		// 2. (SELECT pk from idx1 WHERE prefix_eq(`__mo_index_idx_col`,serial_full("0","value1")) )
    50  		//    	INNER JOIN
    51  		//    (SELECT pk from idx2 WHERE prefix_eq(`__mo_index_idx_col` =  serial_full("1","value2")) )
    52  		//    	ON idx1.pk = idx2.pk
    53  		//    ...
    54  		lastNodeId = currScanId
    55  		currIndexPkCol := &Expr{
    56  			Typ: pkType,
    57  			Expr: &plan.Expr_Col{
    58  				Col: &plan.ColRef{
    59  					RelPos: currIdxProjTag,
    60  					ColPos: 0, // __mo_index_pk_col
    61  				},
    62  			},
    63  		}
    64  		if i != 0 {
    65  			wherePrevPkEqCurrPk, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*Expr{
    66  				currIndexPkCol,
    67  				prevIndexPkCol,
    68  			})
    69  			lastNodeId = builder.appendNode(&plan.Node{
    70  				NodeType: plan.Node_JOIN,
    71  				JoinType: plan.Node_INNER,
    72  				Children: []int32{currScanId, prevLastNodeId},
    73  				OnList:   []*Expr{wherePrevPkEqCurrPk},
    74  			}, builder.ctxByNode[nodeID])
    75  		}
    76  
    77  		prevIndexPkCol = DeepCopyExpr(currIndexPkCol)
    78  		prevLastNodeId = lastNodeId
    79  	}
    80  	lastNodeFromIndexTbl := builder.qry.Nodes[lastNodeId]
    81  	lastNodeFromIndexTbl.Limit = DeepCopyExpr(scanNode.Limit)
    82  	lastNodeFromIndexTbl.Offset = DeepCopyExpr(scanNode.Offset)
    83  	scanNode.Limit, scanNode.Offset = nil, nil
    84  
    85  	// 3. SELECT * from tbl INNER JOIN (
    86  	//    	(SELECT pk from idx1 WHERE prefix_eq(`__mo_index_idx_col`,serial_full("0","value1")) )
    87  	//    		INNER JOIN
    88  	//    	(SELECT pk from idx2 WHERE prefix_eq(`__mo_index_idx_col`,serial_full("1","value2")) )
    89  	//    		ON idx1.pk = idx2.pk
    90  	//    ) ON tbl.pk = idx1.pk
    91  	wherePkEqPk, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*Expr{
    92  		{
    93  			Typ: pkType,
    94  			Expr: &plan.Expr_Col{
    95  				Col: &plan.ColRef{
    96  					RelPos: scanNode.BindingTags[0],
    97  					ColPos: pkPos, // tbl.pk
    98  				},
    99  			},
   100  		},
   101  		{
   102  			Typ: pkType,
   103  			Expr: &plan.Expr_Col{
   104  				Col: &plan.ColRef{
   105  					RelPos: prevIndexPkCol.GetCol().RelPos, // last idxTbl (may be join) relPos
   106  					ColPos: 0,                              // idxTbl.pk
   107  				},
   108  			},
   109  		},
   110  	})
   111  	lastNodeId = builder.appendNode(&plan.Node{
   112  		NodeType: plan.Node_JOIN,
   113  		JoinType: plan.Node_INDEX,
   114  		Children: []int32{scanNode.NodeId, lastNodeId},
   115  		OnList:   []*Expr{wherePkEqPk},
   116  		Limit:    DeepCopyExpr(lastNodeFromIndexTbl.Limit),
   117  		Offset:   DeepCopyExpr(lastNodeFromIndexTbl.Offset),
   118  	}, builder.ctxByNode[nodeID])
   119  
   120  	return lastNodeId
   121  }
   122  
   123  func makeIndexTblScan(builder *QueryBuilder, bindCtx *BindContext, filterExp *plan.Expr,
   124  	idxTableDef *TableDef, idxObjRef *ObjectRef, scanSnapshot *Snapshot, colDefs []*plan.ColDef) (int32, int32) {
   125  
   126  	// a. Scan * WHERE prefix_eq(`__mo_index_idx_col`,serial_full("0","value"))
   127  	idxScanTag := builder.genNewTag()
   128  	args := filterExp.GetF().Args
   129  
   130  	var filterList *plan.Expr
   131  	indexKeyCol := &plan.Expr{
   132  		Typ: makePlan2Type(&varcharType),
   133  		Expr: &plan.Expr_Col{
   134  			Col: &plan.ColRef{
   135  				RelPos: idxScanTag, //__mo_index_idx_col
   136  				ColPos: 0,
   137  			},
   138  		},
   139  	}
   140  
   141  	switch filterExp.GetF().Func.ObjName {
   142  	case "=":
   143  		serialExpr1, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "serial_full",
   144  			[]*plan.Expr{
   145  				makePlan2StringConstExprWithType(getColSeqFromColDef(colDefs[args[0].GetCol().GetColPos()])), // "0"
   146  				args[1], // value
   147  			})
   148  
   149  		filterList, _ = BindFuncExprImplByPlanExpr(builder.GetContext(), "prefix_eq", []*Expr{
   150  			indexKeyCol, // __mo_index_idx_col
   151  			serialExpr1, // serial_full("0","value")
   152  		})
   153  	case "between":
   154  		serialExpr1, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "serial_full", []*plan.Expr{
   155  			makePlan2StringConstExprWithType(getColSeqFromColDef(colDefs[args[0].GetCol().GetColPos()])), // "0"
   156  			args[1], // value1
   157  		})
   158  		serialExpr2, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "serial_full", []*plan.Expr{
   159  			makePlan2StringConstExprWithType(getColSeqFromColDef(colDefs[args[0].GetCol().GetColPos()])), // "0"
   160  			args[2], // value2
   161  		})
   162  		filterList, _ = bindFuncExprAndConstFold(builder.GetContext(), builder.compCtx.GetProcess(), "prefix_between", []*Expr{
   163  			indexKeyCol, // __mo_index_idx_col
   164  			serialExpr1, // serial_full("0","value1")
   165  			serialExpr2, // serial_full("0","value2")
   166  		})
   167  
   168  	case "in":
   169  		// Since this master index specifically for varchar, we assume the `IN` to contain only varchar values.
   170  		inVecType := types.T_varchar.ToType()
   171  
   172  		// a. varchar vector ("value1", "value2", "value3")
   173  		arg1AsColValuesVec := vector.NewVec(inVecType)
   174  		_ = arg1AsColValuesVec.UnmarshalBinary(args[1].GetVec().GetData())
   175  		inExprListLen := arg1AsColValuesVec.Length()
   176  
   177  		// b. const vector "0"
   178  		mp := mpool.MustNewZero()
   179  		arg0AsColNameVec, _ := vector.NewConstBytes(inVecType, []byte(getColSeqFromColDef(colDefs[args[0].GetCol().GetColPos()])), inExprListLen, mp)
   180  
   181  		// c. (serial_full("0","value1"), serial_full("0","value2"), serial_full("0","value3"))
   182  		ps := types.NewPackerArray(inExprListLen, mp)
   183  		defer func() {
   184  			for _, p := range ps {
   185  				p.FreeMem()
   186  			}
   187  		}()
   188  		function.SerialHelper(arg0AsColNameVec, nil, ps, true)
   189  		function.SerialHelper(arg1AsColValuesVec, nil, ps, true)
   190  		arg1ForPrefixInVec := vector.NewVec(inVecType)
   191  		for i := 0; i < inExprListLen; i++ {
   192  			_ = vector.AppendBytes(arg1ForPrefixInVec, ps[i].Bytes(), false, mp)
   193  		}
   194  
   195  		// d. convert result vector to LiteralVec
   196  		arg1ForPrefixInBytes, _ := arg1ForPrefixInVec.MarshalBinary()
   197  		arg1ForPrefixInLitVec := &plan.Expr{
   198  			Typ: makePlan2Type(&varcharType),
   199  			Expr: &plan.Expr_Vec{
   200  				Vec: &plan.LiteralVec{
   201  					Len:  int32(len(arg1ForPrefixInBytes)),
   202  					Data: arg1ForPrefixInBytes,
   203  				},
   204  			},
   205  		}
   206  
   207  		// e. free memory for arg0, arg1 vector. Packer's memory is freed in defer.
   208  		arg1ForPrefixInVec.Free(mp)
   209  		arg0AsColNameVec.Free(mp)
   210  
   211  		filterList, _ = bindFuncExprAndConstFold(builder.GetContext(), builder.compCtx.GetProcess(), "prefix_in", []*Expr{
   212  			indexKeyCol,           // __mo_index_idx_col
   213  			arg1ForPrefixInLitVec, // (serial_full("0","value1"), serial_full("0","value2"), serial_full("0","value3"))
   214  		})
   215  
   216  	default:
   217  		panic("unsupported filter expression")
   218  	}
   219  
   220  	scanId := builder.appendNode(&Node{
   221  		NodeType:    plan.Node_TABLE_SCAN,
   222  		TableDef:    idxTableDef,
   223  		ObjRef:      idxObjRef,
   224  		FilterList:  []*plan.Expr{filterList},
   225  		BindingTags: []int32{idxScanTag},
   226  		//ScanTS:       &scanTs,
   227  		ScanSnapshot: scanSnapshot,
   228  	}, bindCtx)
   229  
   230  	// b. Project __mo_index_pk_col
   231  	projPkCol := &Expr{
   232  		Typ: makePlan2Type(&varcharType),
   233  		Expr: &plan.Expr_Col{
   234  			Col: &plan.ColRef{
   235  				RelPos: idxScanTag, //__mo_index_pk_col
   236  				ColPos: 1,
   237  			},
   238  		},
   239  	}
   240  	idxProjectTag := builder.genNewTag()
   241  	projectId := builder.appendNode(&Node{
   242  		NodeType:    plan.Node_PROJECT,
   243  		Children:    []int32{scanId},
   244  		ProjectList: []*Expr{projPkCol},
   245  		BindingTags: []int32{idxProjectTag},
   246  	}, bindCtx)
   247  
   248  	return idxProjectTag, projectId
   249  }
   250  
   251  func isKeyPresentInList(key string, list []string) bool {
   252  	for _, item := range list {
   253  		if key == item {
   254  			return true
   255  		}
   256  	}
   257  	return false
   258  }