github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/plan/apply_indices_vector.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/catalog" 19 "github.com/matrixorigin/matrixone/pkg/container/types" 20 "github.com/matrixorigin/matrixone/pkg/pb/plan" 21 ) 22 23 var ( 24 /* 25 ### Common Mistakes and Troubleshooting Tips: 26 1. If you use 2 Project's : one col[i] and other l2_distance(col[i]), make sure that l2_distance gets the Deep copy 27 of the col[i]. 28 2. If a plan doesn't work, try using idxColMap and early return to see if the plan works on each stage. 29 3. Feel free to check out the builder.Query.Nodes to see if the plan is being built correctly. 30 31 32 ### NOTES: 33 1. INDEX JOIN Limit Rules: 34 2. Nodes that require BindingTags: TableScan, Project 35 */ 36 distFuncOpTypes = map[string]string{ 37 "l2_distance": "vector_l2_ops", 38 "inner_product": "vector_ip_ops", 39 "cosine_distance": "vector_cosine_ops", 40 } 41 textType = types.T_text.ToType() // return type of @probe_limit 42 ) 43 44 // You replace Sort Node with a new Project Node 45 func (builder *QueryBuilder) applyIndicesForSortUsingVectorIndex(nodeID int32, projNode, sortNode, scanNode *plan.Node, 46 colRefCnt map[[2]int32]int, idxColMap map[[2]int32]*plan.Expr, multiTableIndexWithSortDistFn *MultiTableIndex, 47 colPosOrderBy int32) int32 { 48 49 var pkPos = scanNode.TableDef.Name2ColIndex[scanNode.TableDef.Pkey.PkeyColName] //TODO: watch out. 50 51 distFnExpr := sortNode.OrderBy[0].Expr.GetF() 52 sortDirection := sortNode.OrderBy[0].Flag // For the most part, it is ASC 53 54 // 1.a if any of the other columns in the table are referenced, skip 55 //for i := range scanNode.TableDef.Cols { 56 // if i != int(colPosOrderBy) && colRefCnt[[2]int32{scanNode.BindingTags[0], int32(i)}] > 0 { 57 // goto END0 //TODO: need to understand this part for Aungr 58 // } 59 //} 60 //TODO: selectivity rule. 61 62 // 1.b Check the order by column has refCount > len(sortNode.OrderBy) 63 //colCntOrderBy := colRefCnt[[2]int32{scanNode.BindingTags[0], colPosOrderBy}] - len(sortNode.OrderBy) 64 //if colCntOrderBy > 0 { 65 // //goto END0 //TODO: need to understand this part for Aungr 66 //} 67 68 // 2.a idxTags, idxObjRefs and idxTableDefs 69 var idxTags = make(map[string]int32) 70 var idxObjRefs = make([]*ObjectRef, 3) 71 var idxTableDefs = make([]*TableDef, 3) 72 idxTags["meta.scan"] = builder.genNewTag() 73 idxTags["centroids.scan"] = builder.genNewTag() 74 idxTags["entries.scan"] = builder.genNewTag() 75 // TODO: plan node should hold snapshot info and account info 76 //idxObjRefs[0], idxTableDefs[0] = builder.compCtx.Resolve(scanNode.ObjRef.SchemaName, multiTableIndexWithSortDistFn.IndexDefs[catalog.SystemSI_IVFFLAT_TblType_Metadata].IndexTableName, *scanNode.ScanTS) 77 //idxObjRefs[1], idxTableDefs[1] = builder.compCtx.Resolve(scanNode.ObjRef.SchemaName, multiTableIndexWithSortDistFn.IndexDefs[catalog.SystemSI_IVFFLAT_TblType_Centroids].IndexTableName, *scanNode.ScanTS) 78 //idxObjRefs[2], idxTableDefs[2] = builder.compCtx.Resolve(scanNode.ObjRef.SchemaName, multiTableIndexWithSortDistFn.IndexDefs[catalog.SystemSI_IVFFLAT_TblType_Entries].IndexTableName, *scanNode.ScanTS) 79 80 scanSnapshot := scanNode.ScanSnapshot 81 if scanSnapshot == nil { 82 scanSnapshot = &Snapshot{} 83 } 84 85 idxObjRefs[0], idxTableDefs[0] = builder.compCtx.Resolve(scanNode.ObjRef.SchemaName, multiTableIndexWithSortDistFn.IndexDefs[catalog.SystemSI_IVFFLAT_TblType_Metadata].IndexTableName, *scanSnapshot) 86 idxObjRefs[1], idxTableDefs[1] = builder.compCtx.Resolve(scanNode.ObjRef.SchemaName, multiTableIndexWithSortDistFn.IndexDefs[catalog.SystemSI_IVFFLAT_TblType_Centroids].IndexTableName, *scanSnapshot) 87 idxObjRefs[2], idxTableDefs[2] = builder.compCtx.Resolve(scanNode.ObjRef.SchemaName, multiTableIndexWithSortDistFn.IndexDefs[catalog.SystemSI_IVFFLAT_TblType_Entries].IndexTableName, *scanSnapshot) 88 89 builder.addNameByColRef(idxTags["meta.scan"], idxTableDefs[0]) 90 builder.addNameByColRef(idxTags["centroids.scan"], idxTableDefs[1]) 91 builder.addNameByColRef(idxTags["entries.scan"], idxTableDefs[2]) 92 93 // 2.b Create Centroids.Version == cast(MetaTable.Version) 94 // Order By L2 Distance(centroids, input_literal) ASC limit @probe_limit 95 metaForCurrVersion1, castMetaValueColToBigInt, _ := makeMetaTblScanWhereKeyEqVersionAndCastVersion(builder, builder.ctxByNode[nodeID], 96 idxTableDefs, idxObjRefs, idxTags, "meta") 97 centroidsForCurrVersionAndProbeLimit, _ := makeCentroidsSingleJoinMetaOnCurrVersionOrderByL2DistNormalizeL2(builder, 98 builder.ctxByNode[nodeID], idxTableDefs, idxObjRefs, idxTags, metaForCurrVersion1, distFnExpr, sortDirection, castMetaValueColToBigInt) 99 100 // 2.c Create Entries Node 101 entriesTblScan, _ := makeEntriesTblScan(builder, builder.ctxByNode[nodeID], idxTableDefs, idxObjRefs, idxTags) 102 103 // 2.d Create JOIN entries and centroids on 104 // entries.centroid_id_fk == centroids.centroid_id AND entries.version == centroids.version 105 entriesJoinCentroids := makeEntriesCrossJoinCentroidsOnCentroidId(builder, builder.ctxByNode[nodeID], 106 idxTableDefs, idxTags, 107 entriesTblScan, centroidsForCurrVersionAndProbeLimit) 108 109 // If scan node has no filter condition, then 2 fast path's can be taken. 110 // Path 1: Only use Index Table if Projection Columns are present in Index Table or Constants. 111 // Path 2: May be use INDEX JOIN (not working yet) 112 if scanNode.FilterList == nil { 113 // 3.a Sort By entries by l2_distance(vector_col, literal) ASC limit original_limit 114 sortTblByL2Distance := makeEntriesOrderByL2Distance(builder, builder.ctxByNode[nodeID], distFnExpr, entriesJoinCentroids, sortDirection, idxTableDefs, idxTags, 115 sortNode) 116 117 // Plan 1: Index-Table only Plan 118 { 119 120 // 3.a.1 Check if all the columns in the projection are present in Index Table or Constants. 121 useIndexTablesOnly := true 122 for _, projExp := range projNode.ProjectList { 123 if isRuntimeConstExpr(projExp) { 124 continue 125 } 126 127 if projExp.GetCol() != nil { 128 if projExp.GetCol().ColPos == pkPos { 129 continue 130 } 131 if projExp.GetCol().ColPos == colPosOrderBy { 132 continue 133 } 134 } 135 useIndexTablesOnly = false 136 break 137 } 138 139 // 3.a.2 If all the columns in the projection are present in Index Table or Constants, then use Index Tables only. 140 if useIndexTablesOnly { 141 idxColMap[[2]int32{scanNode.BindingTags[0], pkPos}] = &plan.Expr{ 142 Typ: idxTableDefs[2].Cols[2].Typ, 143 Expr: &plan.Expr_Col{ 144 Col: &plan.ColRef{ 145 RelPos: idxTags["entries.scan"], 146 ColPos: 2, // entries.pk 147 }, 148 }, 149 } 150 idxColMap[[2]int32{scanNode.BindingTags[0], colPosOrderBy}] = &plan.Expr{ 151 Typ: idxTableDefs[2].Cols[3].Typ, 152 Expr: &plan.Expr_Col{ 153 Col: &plan.ColRef{ 154 RelPos: idxTags["entries.scan"], 155 ColPos: 3, // entries.entry 156 }, 157 }, 158 } 159 160 return sortTblByL2Distance 161 } 162 } 163 164 //// Plan 2: Create tbl "INDEX JOIN" entries on entries.original_pk == tbl.pk 165 //{ 166 // // 3.b.1 Create Table "INDEX JOIN" entries on entries.original_pk == tbl.pk. This should only work 167 // // when we don't have any filter condition on the scan node. 168 // projectTbl := makeTblIndexJoinEntriesCentroidOnPK(builder, builder.ctxByNode[nodeID], 169 // idxTableDefs, idxTags, 170 // scanNode, sortTblByL2Distance, pkPos, sortNode) 171 // 172 // return projectTbl 173 //} 174 175 } 176 177 // Path 3: Generic Plan which works for all cases. 178 { 179 180 // 1. Do Entries INNER JOIN Centroids 181 tlbJoinEntries := makeTblInnerJoinEntriesCentroidOnPK(builder, builder.ctxByNode[nodeID], 182 idxTableDefs, idxTags, 183 scanNode, entriesJoinCentroids, pkPos) 184 185 // 2. Do Sort by L2 Distance 186 sortTblByL2Distance := makeInnerJoinOrderByL2Distance(builder, builder.ctxByNode[nodeID], 187 distFnExpr, tlbJoinEntries, sortDirection, idxTableDefs, idxTags, sortNode) 188 189 return sortTblByL2Distance 190 } 191 } 192 193 func makeMetaTblScanWhereKeyEqVersionAndCastVersion(builder *QueryBuilder, bindCtx *BindContext, 194 indexTableDefs []*TableDef, idxRefs []*ObjectRef, idxTags map[string]int32, prefix string) (int32, *Expr, error) { 195 196 // 1. Scan <key, value> from meta table 197 metaTableScanId, scanCols, _ := makeHiddenTblScanWithBindingTag(builder, bindCtx, indexTableDefs[0], idxRefs[0], idxTags[prefix+".scan"]) 198 199 // 2. WHERE key = 'version' 200 whereKeyEqVersion, err := BindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*Expr{ 201 scanCols[0], // key 202 MakePlan2StringConstExprWithType("version"), // "version" 203 }) 204 if err != nil { 205 return -1, nil, err 206 } 207 metaScanNode := builder.qry.Nodes[metaTableScanId] 208 metaScanNode.FilterList = []*Expr{whereKeyEqVersion} 209 210 // 3. Project "value column" as BigInt 211 castMetaValueColToBigInt, err := makePlan2CastExpr(builder.GetContext(), scanCols[1], makePlan2Type(&bigIntType)) 212 if err != nil { 213 return -1, nil, err 214 } 215 216 return metaTableScanId, castMetaValueColToBigInt, nil 217 } 218 219 func makeCentroidsSingleJoinMetaOnCurrVersionOrderByL2DistNormalizeL2(builder *QueryBuilder, bindCtx *BindContext, 220 indexTableDefs []*TableDef, idxRefs []*ObjectRef, idxTags map[string]int32, 221 metaTableScanId int32, distFnExpr *plan.Function, sortDirection plan.OrderBySpec_OrderByFlag, castMetaValueColToBigInt *Expr) (int32, error) { 222 223 // 1. Scan <version, centroid_id, centroid> from centroids table 224 centroidsScanId, scanCols, _ := makeHiddenTblScanWithBindingTag(builder, bindCtx, indexTableDefs[1], idxRefs[1], 225 idxTags["centroids.scan"]) 226 227 //2. JOIN centroids and meta on version 228 joinCond, err := BindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*Expr{ 229 scanCols[0], // centroids.version 230 castMetaValueColToBigInt, // cast(meta.value as BIGINT) 231 }) 232 if err != nil { 233 return -1, err 234 } 235 joinMetaAndCentroidsId := builder.appendNode(&plan.Node{ 236 NodeType: plan.Node_JOIN, 237 JoinType: plan.Node_INNER, 238 Children: []int32{centroidsScanId, metaTableScanId}, 239 OnList: []*Expr{joinCond}, 240 }, bindCtx) 241 242 // 3. Build Projection for l2_distance(centroid, normalize_l2(literal)) 243 centroidsCol := &plan.Expr{ 244 Typ: indexTableDefs[1].Cols[2].Typ, 245 Expr: &plan.Expr_Col{ 246 Col: &plan.ColRef{ 247 RelPos: idxTags["centroids.scan"], 248 ColPos: 2, 249 }, 250 }, 251 } 252 normalizeL2Lit, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "normalize_l2", []*plan.Expr{ 253 distFnExpr.Args[1], 254 }) 255 distFnName := distFnExpr.Func.ObjName 256 l2DistanceLitNormalizeL2Col, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), distFnName, []*plan.Expr{ 257 centroidsCol, // centroid 258 normalizeL2Lit, // normalize_l2(literal) 259 }) 260 261 // 4. Sort by l2_distance(centroid, normalize_l2(literal)) limit @probe_limit 262 // 4.1 @probe_limit is a system variable 263 probeLimitValueExpr := &plan.Expr{ 264 Typ: makePlan2Type(&textType), // T_text 265 Expr: &plan.Expr_V{ 266 V: &plan.VarRef{ 267 Name: "probe_limit", 268 Global: false, 269 System: false, 270 }, 271 }, 272 } 273 274 //4.2 ISNULL(@var) 275 arg0, err := BindFuncExprImplByPlanExpr(builder.GetContext(), "isnull", []*plan.Expr{ 276 probeLimitValueExpr, 277 }) 278 if err != nil { 279 return -1, err 280 } 281 282 // 4.3 CAST( 1 AS BIGINT) 283 arg1 := makePlan2Int64ConstExprWithType(1) 284 285 // 4.4 CAST(@var AS BIGINT) 286 targetType := types.T_int64.ToType() 287 planTargetType := makePlan2Type(&targetType) 288 arg2, err := appendCastBeforeExpr(builder.GetContext(), probeLimitValueExpr, planTargetType) 289 if err != nil { 290 return -1, err 291 } 292 293 ifNullLimitExpr, err := BindFuncExprImplByPlanExpr(builder.GetContext(), "case", []*plan.Expr{ 294 arg0, 295 arg1, 296 arg2, 297 }) 298 if err != nil { 299 return -1, err 300 } 301 302 sortCentroidsByL2DistanceId := builder.appendNode(&plan.Node{ 303 NodeType: plan.Node_SORT, 304 Children: []int32{joinMetaAndCentroidsId}, 305 Limit: ifNullLimitExpr, 306 OrderBy: []*OrderBySpec{ 307 { 308 Expr: l2DistanceLitNormalizeL2Col, 309 Flag: sortDirection, 310 }, 311 }, 312 }, bindCtx) 313 314 return sortCentroidsByL2DistanceId, nil 315 } 316 317 func makeEntriesTblScan(builder *QueryBuilder, bindCtx *BindContext, indexTableDefs []*TableDef, idxRefs []*ObjectRef, idxTags map[string]int32) (int32, error) { 318 319 // 1. Scan <version, centroid_id_fk, origin_pk, embedding> from entries table 320 entriesScanId, _, _ := makeHiddenTblScanWithBindingTag(builder, bindCtx, indexTableDefs[2], idxRefs[2], 321 idxTags["entries.scan"]) 322 323 return entriesScanId, nil 324 } 325 326 func makeEntriesCrossJoinCentroidsOnCentroidId(builder *QueryBuilder, bindCtx *BindContext, idxTableDefs []*TableDef, 327 idxTags map[string]int32, entries int32, centroidsForCurrVersion int32) int32 { 328 329 centroidVersionEqEntriesVersion, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*Expr{ 330 { 331 Typ: idxTableDefs[2].Cols[0].Typ, 332 Expr: &plan.Expr_Col{ 333 Col: &plan.ColRef{ 334 RelPos: idxTags["entries.scan"], 335 ColPos: 0, // entries.__mo_version 336 }, 337 }, 338 }, 339 { 340 Typ: idxTableDefs[1].Cols[0].Typ, 341 Expr: &plan.Expr_Col{ 342 Col: &plan.ColRef{ 343 RelPos: idxTags["centroids.scan"], 344 ColPos: 0, // centroids.__mo_version 345 }, 346 }, 347 }, 348 }) 349 350 entriesCentroidIdEqCentroidId, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*Expr{ 351 { 352 Typ: idxTableDefs[2].Cols[1].Typ, 353 Expr: &plan.Expr_Col{ 354 Col: &plan.ColRef{ 355 RelPos: idxTags["entries.scan"], 356 ColPos: 1, // entries.__mo_index_centroid_fk_id 357 }, 358 }, 359 }, 360 { 361 Typ: idxTableDefs[1].Cols[1].Typ, 362 Expr: &plan.Expr_Col{ 363 Col: &plan.ColRef{ 364 RelPos: idxTags["centroids.scan"], 365 ColPos: 1, // centroids.__mo_index_centroid_id 366 }, 367 }, 368 }, 369 }) 370 371 var onList = []*Expr{entriesCentroidIdEqCentroidId, centroidVersionEqEntriesVersion} 372 // Create JOIN entries and centroids 373 // ON 374 // - centroids.centroid_id == entries.centroid_id_fk AND 375 // - centroids.version == entries.version 376 joinEntriesAndCentroids := builder.appendNode(&plan.Node{ 377 NodeType: plan.Node_JOIN, 378 JoinType: plan.Node_SEMI, 379 Children: []int32{entries, centroidsForCurrVersion}, 380 OnList: onList, 381 }, bindCtx) 382 383 return joinEntriesAndCentroids 384 } 385 386 //TODO: fix it later 387 //func makeTblIndexJoinEntriesCentroidOnPK(builder *QueryBuilder, bindCtx *BindContext, 388 // idxTableDefs []*TableDef, idxTags map[string]int32, 389 // scanNode *plan.Node, entriesJoinCentroids int32, pkPos int32, 390 // sortNode *plan.Node) int32 { 391 // 392 // entriesOriginPkEqTblPk, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*Expr{ 393 // 394 // { 395 // Typ: idxTableDefs[2].Cols[2].Typ, 396 // Expr: &plan.Expr_Col{ 397 // Col: &plan.ColRef{ 398 // RelPos: scanNode.BindingTags[0], 399 // ColPos: pkPos, // tbl.pk 400 // }, 401 // }, 402 // }, 403 // { 404 // Typ: idxTableDefs[2].Cols[2].Typ, 405 // Expr: &plan.Expr_Col{ 406 // Col: &plan.ColRef{ 407 // RelPos: idxTags["entries.scan"], 408 // ColPos: 2, // entries.origin_pk 409 // }, 410 // }, 411 // }, 412 // }) 413 // entriesJoinTbl := builder.appendNode(&plan.Node{ 414 // NodeType: plan.Node_JOIN, 415 // JoinType: plan.Node_INDEX, 416 // Children: []int32{scanNode.NodeId, entriesJoinCentroids}, 417 // OnList: []*Expr{entriesOriginPkEqTblPk}, 418 // }, bindCtx) 419 // 420 // return entriesJoinTbl 421 //} 422 423 func makeTblInnerJoinEntriesCentroidOnPK(builder *QueryBuilder, bindCtx *BindContext, 424 idxTableDefs []*TableDef, idxTags map[string]int32, 425 scanNode *plan.Node, entriesJoinCentroids int32, pkPos int32) int32 { 426 427 entriesOriginPkEqTblPk, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*Expr{ 428 429 { 430 Typ: idxTableDefs[2].Cols[2].Typ, 431 Expr: &plan.Expr_Col{ 432 Col: &plan.ColRef{ 433 RelPos: scanNode.BindingTags[0], 434 ColPos: pkPos, // tbl.pk 435 }, 436 }, 437 }, 438 { 439 Typ: idxTableDefs[2].Cols[2].Typ, 440 Expr: &plan.Expr_Col{ 441 Col: &plan.ColRef{ 442 RelPos: idxTags["entries.scan"], 443 ColPos: 2, // entries.origin_pk 444 }, 445 }, 446 }, 447 }) 448 entriesJoinTbl := builder.appendNode(&plan.Node{ 449 NodeType: plan.Node_JOIN, 450 JoinType: plan.Node_INNER, 451 Children: []int32{scanNode.NodeId, entriesJoinCentroids}, 452 OnList: []*Expr{entriesOriginPkEqTblPk}, 453 }, bindCtx) 454 455 return entriesJoinTbl 456 } 457 458 func makeEntriesOrderByL2Distance(builder *QueryBuilder, bindCtx *BindContext, 459 fn *plan.Function, entriesJoinCentroids int32, 460 sortDirection plan.OrderBySpec_OrderByFlag, 461 idxTableDefs []*TableDef, idxTags map[string]int32, 462 sortNode *plan.Node) int32 { 463 464 distFnName := fn.Func.ObjName 465 l2DistanceColLit, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), distFnName, []*plan.Expr{ 466 { 467 Typ: idxTableDefs[2].Cols[3].Typ, 468 Expr: &plan.Expr_Col{ 469 Col: &plan.ColRef{ 470 RelPos: idxTags["entries.scan"], 471 ColPos: 3, // entries.entry 472 }, 473 }, 474 }, 475 fn.Args[1], // lit 476 }) 477 sortTblByL2Distance := builder.appendNode(&plan.Node{ 478 NodeType: plan.Node_SORT, 479 Children: []int32{entriesJoinCentroids}, 480 Limit: DeepCopyExpr(sortNode.Limit), 481 Offset: DeepCopyExpr(sortNode.Offset), 482 OrderBy: []*OrderBySpec{ 483 { 484 Expr: l2DistanceColLit, 485 Flag: sortDirection, 486 }, 487 }, 488 }, bindCtx) 489 return sortTblByL2Distance 490 } 491 492 func makeInnerJoinOrderByL2Distance(builder *QueryBuilder, bindCtx *BindContext, 493 fn *plan.Function, tlbJoinEntries int32, 494 sortDirection plan.OrderBySpec_OrderByFlag, 495 idxTableDefs []*TableDef, idxTags map[string]int32, 496 sortNode *plan.Node) int32 { 497 498 distFnName := fn.Func.ObjName 499 l2DistanceColLit, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), distFnName, []*plan.Expr{ 500 { 501 Typ: idxTableDefs[2].Cols[3].Typ, 502 Expr: &plan.Expr_Col{ 503 Col: &plan.ColRef{ 504 RelPos: idxTags["entries.scan"], 505 ColPos: 3, // entries.entry 506 }, 507 }, 508 }, 509 fn.Args[1], // lit 510 }) 511 sortTblByL2Distance := builder.appendNode(&plan.Node{ 512 NodeType: plan.Node_SORT, 513 Children: []int32{tlbJoinEntries}, 514 Limit: DeepCopyExpr(sortNode.Limit), 515 Offset: DeepCopyExpr(sortNode.Offset), 516 OrderBy: []*OrderBySpec{ 517 { 518 Expr: l2DistanceColLit, 519 Flag: sortDirection, 520 }, 521 }, 522 }, bindCtx) 523 return sortTblByL2Distance 524 } 525 526 func makeHiddenTblScanWithBindingTag(builder *QueryBuilder, bindCtx *BindContext, 527 indexTableDef *TableDef, idxObjRef *ObjectRef, idxTag int32) (int32, []*Expr, *Node) { 528 529 // 1. Create Scan 530 scanId := builder.appendNode(&Node{ 531 NodeType: plan.Node_TABLE_SCAN, 532 TableDef: indexTableDef, 533 ObjRef: idxObjRef, 534 BindingTags: []int32{idxTag}, 535 }, bindCtx) 536 537 // 2. Create Scan Cols 538 scanCols := make([]*Expr, len(indexTableDef.Cols)) 539 for colIdx, column := range indexTableDef.Cols { 540 scanCols[colIdx] = &plan.Expr{ 541 Typ: column.Typ, 542 Expr: &plan.Expr_Col{ 543 Col: &plan.ColRef{ 544 RelPos: idxTag, 545 ColPos: int32(colIdx), 546 Name: column.Name, 547 }, 548 }, 549 } 550 } 551 return scanId, scanCols, nil 552 } 553 554 func (builder *QueryBuilder) resolveScanNodeWithIndex(node *plan.Node, depth int32) *plan.Node { 555 if depth == 0 { 556 if node.NodeType == plan.Node_TABLE_SCAN && node.TableDef.Indexes != nil { 557 return node 558 } 559 return nil 560 } 561 562 if node.NodeType == plan.Node_SORT && len(node.Children) == 1 { 563 return builder.resolveScanNodeWithIndex(builder.qry.Nodes[node.Children[0]], depth-1) 564 } 565 566 return nil 567 } 568 569 func (builder *QueryBuilder) resolveSortNode(node *plan.Node, depth int32) *plan.Node { 570 if depth == 0 { 571 if node.NodeType == plan.Node_SORT { 572 return node 573 } 574 return nil 575 } 576 577 if node.NodeType == plan.Node_PROJECT && len(node.Children) == 1 { 578 return builder.resolveSortNode(builder.qry.Nodes[node.Children[0]], depth-1) 579 } 580 581 return nil 582 }