From 911d5a1cd8d92008d425a74d6752cf823f5e9b61 Mon Sep 17 00:00:00 2001 From: Zequan <110292382+EricZequan@users.noreply.github.com> Date: Mon, 6 Jan 2025 12:25:58 +0800 Subject: [PATCH] planner, executor: support inline projection for TopN (#58500) ref pingcap/tidb#54245 --- pkg/executor/benchmark_test.go | 134 +++++ pkg/executor/builder.go | 44 +- pkg/executor/sortexec/topn.go | 10 +- pkg/executor/sortexec/topn_chunk_heap.go | 14 +- .../cbotest/testdata/analyze_suite_out.json | 11 +- .../casetest/dag/testdata/plan_suite_out.json | 22 +- .../core/casetest/vectorsearch/BUILD.bazel | 2 +- .../testdata/ann_index_suite_in.json | 28 + .../testdata/ann_index_suite_out.json | 520 +++++++++++++----- .../vectorsearch/vector_index_test.go | 53 +- pkg/planner/core/exhaust_physical_plans.go | 2 + .../logicalop/hash64_equals_generated.go | 4 + .../core/operator/logicalop/logical_top_n.go | 7 +- pkg/planner/core/physical_plans.go | 6 +- pkg/planner/core/plan_clone_generated.go | 4 +- pkg/planner/core/resolve_indices.go | 60 +- pkg/planner/core/task.go | 8 +- pkg/util/chunk/chunk.go | 24 + pkg/util/chunk/chunk_test.go | 40 ++ .../r/executor/index_merge_reader.result | 26 +- tests/integrationtest/r/explain_easy.result | 11 +- .../r/globalindex/mem_index_merge.result | 26 +- .../planner/core/casetest/integration.result | 26 +- .../partition/integration_partition.result | 321 +++++------ .../casetest/rule/rule_result_reorder.result | 9 +- .../core/issuetest/planner_issue.result | 30 +- tests/integrationtest/r/select.result | 13 +- 27 files changed, 1007 insertions(+), 448 deletions(-) diff --git a/pkg/executor/benchmark_test.go b/pkg/executor/benchmark_test.go index 46bc74212c6a5..9000fd7b79742 100644 --- a/pkg/executor/benchmark_test.go +++ b/pkg/executor/benchmark_test.go @@ -1853,6 +1853,140 @@ func BenchmarkLimitExec(b *testing.B) { } } +type topNTestCase struct { + rows int + offset int + count int + orderByIdx []int + usingInlineProjection bool + columnIdxsUsedByChild []bool + ctx sessionctx.Context +} + +func (tc topNTestCase) columns() []*expression.Column { + return []*expression.Column{ + {Index: 0, RetType: types.NewFieldType(mysql.TypeLonglong)}, + {Index: 1, RetType: types.NewFieldType(mysql.TypeLonglong)}, + {Index: 2, RetType: types.NewFieldType(mysql.TypeLonglong)}, + } +} + +func (tc topNTestCase) String() string { + return fmt.Sprintf("(rows:%v, offset:%v, count:%v, orderByIdx:%v, inline_projection:%v)", + tc.rows, tc.offset, tc.count, tc.orderByIdx, tc.usingInlineProjection) +} + +func defaultTopNTestCase() *topNTestCase { + ctx := mock.NewContext() + ctx.GetSessionVars().InitChunkSize = variable.DefInitChunkSize + ctx.GetSessionVars().MaxChunkSize = variable.DefMaxChunkSize + ctx.GetSessionVars().StmtCtx.MemTracker = memory.NewTracker(-1, -1) + return &topNTestCase{ + rows: 100000, + offset: 0, + count: 10, + orderByIdx: []int{0}, + usingInlineProjection: false, + columnIdxsUsedByChild: []bool{false, true, false}, + ctx: ctx, + } +} + +func benchmarkTopNExec(b *testing.B, cas *topNTestCase) { + opt := testutil.MockDataSourceParameters{ + DataSchema: expression.NewSchema(cas.columns()...), + Rows: cas.rows, + Ctx: cas.ctx, + } + dataSource := testutil.BuildMockDataSource(opt) + executorSort := sortexec.SortExec{ + BaseExecutor: exec.NewBaseExecutor(cas.ctx, dataSource.Schema(), 4, dataSource), + ByItems: make([]*util.ByItems, 0, len(cas.orderByIdx)), + ExecSchema: dataSource.Schema(), + } + for _, idx := range cas.orderByIdx { + executorSort.ByItems = append(executorSort.ByItems, &util.ByItems{Expr: cas.columns()[idx]}) + } + + executor := &sortexec.TopNExec{ + SortExec: executorSort, + Limit: &core.PhysicalLimit{ + Count: uint64(cas.count), + Offset: uint64(cas.offset), + }, + } + + executor.ExecSchema = dataSource.Schema().Clone() + + var exe exec.Executor + if cas.usingInlineProjection { + if len(cas.columnIdxsUsedByChild) > 0 { + executor.ColumnIdxsUsedByChild = make([]int, 0, len(cas.columnIdxsUsedByChild)) + for i, used := range cas.columnIdxsUsedByChild { + if used { + executor.ColumnIdxsUsedByChild = append(executor.ColumnIdxsUsedByChild, i) + } + } + } + exe = executor + } else { + columns := cas.columns() + usedCols := make([]*expression.Column, 0, len(columns)) + exprs := make([]expression.Expression, 0, len(columns)) + for i, used := range cas.columnIdxsUsedByChild { + if used { + usedCols = append(usedCols, columns[i]) + exprs = append(exprs, columns[i]) + } + } + proj := &ProjectionExec{ + BaseExecutorV2: exec.NewBaseExecutorV2(cas.ctx.GetSessionVars(), expression.NewSchema(usedCols...), 0, executor), + numWorkers: 1, + evaluatorSuit: expression.NewEvaluatorSuite(exprs, false), + } + exe = proj + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + tmpCtx := context.Background() + chk := exec.NewFirstChunk(exe) + dataSource.PrepareChunks() + + b.StartTimer() + if err := exe.Open(tmpCtx); err != nil { + b.Fatal(err) + } + for { + if err := exe.Next(tmpCtx, chk); err != nil { + b.Fatal(err) + } + if chk.NumRows() == 0 { + break + } + } + + if err := exe.Close(); err != nil { + b.Fatal(err) + } + b.StopTimer() + } +} + +func BenchmarkTopNExec(b *testing.B) { + b.ReportAllocs() + usingInlineProjection := []bool{false, true} + + for _, inlineProjection := range usingInlineProjection { + cas := defaultTopNTestCase() + cas.usingInlineProjection = inlineProjection + b.Run(fmt.Sprintf("TopNExec InlineProjection:%v", inlineProjection), func(b *testing.B) { + benchmarkTopNExec(b, cas) + }) + } +} + func BenchmarkReadLastLinesOfHugeLine(b *testing.B) { // step 1. initial a huge line log file hugeLine := make([]byte, 1024*1024*10) diff --git a/pkg/executor/builder.go b/pkg/executor/builder.go index a4607d0c25ebc..4ca10ebca5efe 100644 --- a/pkg/executor/builder.go +++ b/pkg/executor/builder.go @@ -2491,11 +2491,27 @@ func (b *executorBuilder) buildTopN(v *plannercore.PhysicalTopN) exec.Executor { ExecSchema: v.Schema(), } executor_metrics.ExecutorCounterTopNExec.Inc() - return &sortexec.TopNExec{ + t := &sortexec.TopNExec{ SortExec: sortExec, Limit: &plannercore.PhysicalLimit{Count: v.Count, Offset: v.Offset}, Concurrency: b.ctx.GetSessionVars().Concurrency.ExecutorConcurrency, } + columnIdxsUsedByChild, columnMissing := retrieveColumnIdxsUsedByChild(v.Schema(), v.Children()[0].Schema()) + if columnIdxsUsedByChild != nil && columnMissing { + // In the expected cases colMissing will never happen. + // However, suppose that childSchema contains generatedCol and is cloned by selfSchema. + // Then childSchema.generatedCol.UniqueID will not be equal to selfSchema.generatedCol.UniqueID. + // In this case, colMissing occurs, but it is not wrong. + // So here we cancel the inline projection, take all of columns from child. + // If the inline projection directly generates some error causes colMissing, + // notice that the error feedback given would be inaccurate. + columnIdxsUsedByChild = nil + // TODO: If there is valid verification logic, please uncomment the following code + // b.err = errors.Annotate(ErrBuildExecutor, "Inline projection occurs when `buildTopN` exectutor, columns should not missing in the child schema") + // return nil + } + t.ColumnIdxsUsedByChild = columnIdxsUsedByChild + return t } func (b *executorBuilder) buildApply(v *plannercore.PhysicalApply) exec.Executor { @@ -3181,6 +3197,32 @@ func (b *executorBuilder) buildAnalyze(v *plannercore.Analyze) exec.Executor { return e } +// retrieveColumnIdxsUsedByChild retrieve column indices map from child physical plan schema columns. +// +// E.g. columnIdxsUsedByChild = [2, 3, 1] means child[col2, col3, col1] -> parent[col0, col1, col2]. +// `columnMissing` indicates whether one or more columns in `selfSchema` are not found in `childSchema`. +// And `-1` in `columnIdxsUsedByChild` indicates the column not found. +// If columnIdxsUsedByChild == nil, means selfSchema and childSchema are equal. +func retrieveColumnIdxsUsedByChild(selfSchema *expression.Schema, childSchema *expression.Schema) ([]int, bool) { + equalSchema := (selfSchema.Len() == childSchema.Len()) + columnMissing := false + columnIdxsUsedByChild := make([]int, 0, selfSchema.Len()) + for selfIdx, selfCol := range selfSchema.Columns { + colIdxInChild := childSchema.ColumnIndex(selfCol) + if !columnMissing && colIdxInChild == -1 { + columnMissing = true + } + if equalSchema && selfIdx != colIdxInChild { + equalSchema = false + } + columnIdxsUsedByChild = append(columnIdxsUsedByChild, colIdxInChild) + } + if equalSchema { + columnIdxsUsedByChild = nil + } + return columnIdxsUsedByChild, columnMissing +} + // markChildrenUsedCols compares each child with the output schema, and mark // each column of the child is used by output or not. func markChildrenUsedCols(outputCols []*expression.Column, childSchemas ...*expression.Schema) (childrenUsed [][]int) { diff --git a/pkg/executor/sortexec/topn.go b/pkg/executor/sortexec/topn.go index 68e6fe983e973..9291f881d4d5e 100644 --- a/pkg/executor/sortexec/topn.go +++ b/pkg/executor/sortexec/topn.go @@ -65,6 +65,9 @@ type TopNExec struct { isSpillTriggeredInStage2ForTest bool Concurrency int + + // ColumnIdxsUsedByChild keep column indexes of child executor used for inline projection + ColumnIdxsUsedByChild []int } // Open implements the Executor Open interface. @@ -240,7 +243,12 @@ func (e *TopNExec) Next(ctx context.Context, req *chunk.Chunk) error { if !ok || row.err != nil { return row.err } - req.AppendRow(row.row) + // Be careful, if inline projection occurs. + // TopN's schema may be not match child executor's output columns. + // We should extract only the required columns from child's executor. + // Do not do it on `loadChunksUntilTotalLimit` or `processChildChk`, + // cauz it may destroy the correctness of executor's `keyColumns`. + req.AppendRowsByColIdxs([]chunk.Row{row.row}, e.ColumnIdxsUsedByChild) } } return nil diff --git a/pkg/executor/sortexec/topn_chunk_heap.go b/pkg/executor/sortexec/topn_chunk_heap.go index 4d791b77372ac..94508b2268b84 100644 --- a/pkg/executor/sortexec/topn_chunk_heap.go +++ b/pkg/executor/sortexec/topn_chunk_heap.go @@ -52,7 +52,12 @@ type topNChunkHeap struct { func (h *topNChunkHeap) init(topnExec *TopNExec, memTracker *memory.Tracker, totalLimit uint64, idx int, greaterRow func(chunk.Row, chunk.Row) bool, fieldTypes []*types.FieldType) { h.memTracker = memTracker - h.rowChunks = chunk.NewList(exec.RetTypes(topnExec), topnExec.InitCap(), topnExec.MaxChunkSize()) + // The schema of TopN keep same with its children without inline projection. After inline projection, TopN will have its own schema, + // so TopN can not be used to construct chunks, but children information needs to be used instead. + // Row size of new chunk list may not be enough to hold the result set from child executor when inline projection occurs. + // To avoid this problem, we use child executor's schmea to build new chunk list by default. + ch := topnExec.Children(0) + h.rowChunks = chunk.NewList(exec.RetTypes(ch), ch.InitCap(), ch.MaxChunkSize()) h.rowChunks.GetMemTracker().AttachTo(h.memTracker) h.rowChunks.GetMemTracker().SetLabel(memory.LabelForRowChunks) @@ -112,7 +117,12 @@ func (h *topNChunkHeap) processChk(chk *chunk.Chunk) { // but we want descending top N, then we will keep all data in memory. // But if data is distributed randomly, this function will be called log(n) times. func (h *topNChunkHeap) doCompaction(topnExec *TopNExec) error { - newRowChunks := chunk.NewList(exec.RetTypes(topnExec), topnExec.InitCap(), topnExec.MaxChunkSize()) + // The schema of TopN keep same with its children without inline projection. After inline projection, TopN will have its own schema, + // so TopN can not be used to construct chunks, but children information needs to be used instead. + // Row size of new chunk list may not be enough to hold the result set from child executor when inline projection occurs. + // To avoid this problem, we use child executor's schmea to build new chunk list by default. + ch := topnExec.Children(0) + newRowChunks := chunk.NewList(exec.RetTypes(ch), ch.InitCap(), ch.MaxChunkSize()) newRowPtrs := make([]chunk.RowPtr, 0, h.rowChunks.Len()) for _, rowPtr := range h.rowPtrs { newRowPtr := newRowChunks.AppendRow(h.rowChunks.GetRow(rowPtr)) diff --git a/pkg/planner/core/casetest/cbotest/testdata/analyze_suite_out.json b/pkg/planner/core/casetest/cbotest/testdata/analyze_suite_out.json index a74d16649a4c0..054f0259473b3 100644 --- a/pkg/planner/core/casetest/cbotest/testdata/analyze_suite_out.json +++ b/pkg/planner/core/casetest/cbotest/testdata/analyze_suite_out.json @@ -235,12 +235,11 @@ "explain format = 'brief' SELECT a FROM t WHERE b = 2 and c > 0 ORDER BY a limit 1" ], "Plan": [ - "Projection 1.00 root test.t.a", - "└─TopN 1.00 root test.t.a, offset:0, count:1", - " └─IndexReader 1.00 root index:TopN", - " └─TopN 1.00 cop[tikv] test.t.a, offset:0, count:1", - " └─Selection 6.00 cop[tikv] gt(test.t.c, 0)", - " └─IndexRangeScan 6.00 cop[tikv] table:t, index:idx(b, d, a, c) range:[2,2], keep order:false" + "TopN 1.00 root test.t.a, offset:0, count:1", + "└─IndexReader 1.00 root index:TopN", + " └─TopN 1.00 cop[tikv] test.t.a, offset:0, count:1", + " └─Selection 6.00 cop[tikv] gt(test.t.c, 0)", + " └─IndexRangeScan 6.00 cop[tikv] table:t, index:idx(b, d, a, c) range:[2,2], keep order:false" ] } ] diff --git a/pkg/planner/core/casetest/dag/testdata/plan_suite_out.json b/pkg/planner/core/casetest/dag/testdata/plan_suite_out.json index c173504285016..5269728794a23 100644 --- a/pkg/planner/core/casetest/dag/testdata/plan_suite_out.json +++ b/pkg/planner/core/casetest/dag/testdata/plan_suite_out.json @@ -44,15 +44,15 @@ }, { "SQL": "select c from t where t.c = 1 and t.e = 1 order by t.d limit 1", - "Best": "IndexReader(Index(t.c_d_e)[[1,1]]->Sel([eq(test.t.e, 1)])->Limit)->Limit->Projection" + "Best": "IndexReader(Index(t.c_d_e)[[1,1]]->Sel([eq(test.t.e, 1)])->Limit)->Limit" }, { "SQL": "select c from t order by t.a limit 1", - "Best": "TableReader(Table(t)->Limit)->Limit->Projection" + "Best": "TableReader(Table(t)->Limit)->Limit" }, { "SQL": "select c from t order by t.a + t.b limit 1", - "Best": "TableReader(Table(t)->TopN([plus(test.t.a, test.t.b)],0,1))->Projection->TopN([Column#14],0,1)->Projection->Projection" + "Best": "TableReader(Table(t)->TopN([plus(test.t.a, test.t.b)],0,1))->Projection->TopN([Column#14],0,1)->Projection" }, { "SQL": "select c from t limit 1", @@ -92,11 +92,11 @@ }, { "SQL": "select c from t where t.c = 1 and t.a > 1 order by t.d limit 1", - "Best": "IndexReader(Index(t.c_d_e)[[1,1]]->Sel([gt(test.t.a, 1)])->Limit)->Limit->Projection" + "Best": "IndexReader(Index(t.c_d_e)[[1,1]]->Sel([gt(test.t.a, 1)])->Limit)->Limit" }, { "SQL": "select c from t where t.c = 1 and t.d = 1 order by t.a limit 1", - "Best": "IndexReader(Index(t.c_d_e)[[1 1,1 1]]->TopN([test.t.a],0,1))->TopN([test.t.a],0,1)->Projection" + "Best": "IndexReader(Index(t.c_d_e)[[1 1,1 1]]->TopN([test.t.a],0,1))->TopN([test.t.a],0,1)" }, { "SQL": "select * from t where t.c = 1 and t.a > 1 order by t.d limit 1", @@ -451,7 +451,7 @@ }, { "SQL": "delete from t where b < 1 order by d limit 1", - "Best": "TableReader(Table(t)->Sel([lt(test.t.b, 1)])->TopN([test.t.d],0,1))->TopN([test.t.d],0,1)->Projection->Delete", + "Best": "TableReader(Table(t)->Sel([lt(test.t.b, 1)])->TopN([test.t.d],0,1))->TopN([test.t.d],0,1)->Delete", "Hints": "use_index(@`del_1` `test`.`t` ), no_order_index(@`del_1` `test`.`t` `primary`), limit_to_cop(@`del_1`)" }, { @@ -626,7 +626,7 @@ }, { "SQL": "select count(*) from t group by g order by g limit 10", - "Best": "IndexReader(Index(t.g)[[NULL,+inf]]->StreamAgg)->StreamAgg->Limit->Projection" + "Best": "IndexReader(Index(t.g)[[NULL,+inf]]->StreamAgg)->StreamAgg->Limit" }, { "SQL": "select count(*) from t group by g limit 10", @@ -638,11 +638,11 @@ }, { "SQL": "select count(*) from t group by g order by g desc limit 1", - "Best": "IndexReader(Index(t.g)[[NULL,+inf]]->StreamAgg)->StreamAgg->Limit->Projection" + "Best": "IndexReader(Index(t.g)[[NULL,+inf]]->StreamAgg)->StreamAgg->Limit" }, { "SQL": "select count(*) from t group by b order by b limit 10", - "Best": "TableReader(Table(t)->HashAgg)->HashAgg->TopN([test.t.b],0,10)->Projection" + "Best": "TableReader(Table(t)->HashAgg)->HashAgg->TopN([test.t.b],0,10)" }, { "SQL": "select count(*) from t group by b order by b", @@ -658,11 +658,11 @@ }, { "SQL": "select /*+ tidb_inlj(a,b) */ sum(a.g), sum(b.g) from t a join t b on a.g = b.g and a.g > 60 group by a.g order by a.g limit 1", - "Best": "IndexJoin{IndexReader(Index(t.g)[(60,+inf]])->IndexReader(Index(t.g)[[NULL,NULL]]->Sel([gt(test.t.g, 60)]))}(test.t.g,test.t.g)->Projection->StreamAgg->Limit->Projection" + "Best": "IndexJoin{IndexReader(Index(t.g)[(60,+inf]])->IndexReader(Index(t.g)[[NULL,NULL]]->Sel([gt(test.t.g, 60)]))}(test.t.g,test.t.g)->Projection->StreamAgg->Limit" }, { "SQL": "select sum(a.g), sum(b.g) from t a join t b on a.g = b.g and a.a>5 group by a.g order by a.g limit 1", - "Best": "MergeInnerJoin{IndexReader(Index(t.g)[[NULL,+inf]]->Sel([gt(test.t.a, 5)]))->IndexReader(Index(t.g)[[NULL,+inf]])}(test.t.g,test.t.g)->Projection->StreamAgg->Limit->Projection" + "Best": "MergeInnerJoin{IndexReader(Index(t.g)[[NULL,+inf]]->Sel([gt(test.t.a, 5)]))->IndexReader(Index(t.g)[[NULL,+inf]])}(test.t.g,test.t.g)->Projection->StreamAgg->Limit" }, { "SQL": "select sum(d) from t", diff --git a/pkg/planner/core/casetest/vectorsearch/BUILD.bazel b/pkg/planner/core/casetest/vectorsearch/BUILD.bazel index f0fa36a86baf6..1a9a81c20ebf5 100644 --- a/pkg/planner/core/casetest/vectorsearch/BUILD.bazel +++ b/pkg/planner/core/casetest/vectorsearch/BUILD.bazel @@ -9,7 +9,7 @@ go_test( ], data = glob(["testdata/**"]), flaky = True, - shard_count = 7, + shard_count = 8, deps = [ "//pkg/config", "//pkg/domain", diff --git a/pkg/planner/core/casetest/vectorsearch/testdata/ann_index_suite_in.json b/pkg/planner/core/casetest/vectorsearch/testdata/ann_index_suite_in.json index be40b93f6a9c7..b682d966a5d9a 100644 --- a/pkg/planner/core/casetest/vectorsearch/testdata/ann_index_suite_in.json +++ b/pkg/planner/core/casetest/vectorsearch/testdata/ann_index_suite_in.json @@ -80,5 +80,33 @@ "explain select id, vec_cosine_distance(vec, '[1,1,1]') as d, a, b from t1 order by d limit 10", "explain select id, a, b, vec_cosine_distance(vec, '[1,1,1]') as d from t1 order by d limit 10" ] + }, + { + "name": "TestVectorSearchHeavyFunction", + "cases": [ + "explain select id from t1 order by vec_cosine_distance(vec, '[1,1,1]') limit 10", + "explain select id from t1 order by vec_l1_distance(vec, '[1,1,1]') limit 10", + "explain select id from t1 order by vec_l2_distance(vec, '[1,1,1]') limit 10", + "explain select id from t1 order by vec_negative_inner_product(vec, '[1,1,1]') limit 10", + "explain select id from t1 order by vec_dims(vec) limit 10", + "explain select id from t1 order by vec_l2_norm(vec) limit 10", + "explain select id from t1 order by MOD(a, 3) limit 10", + + "explain select id, vec_cosine_distance(vec, '[1,1,1]') as d from t1 order by d limit 10", + "explain select id, vec_l1_distance(vec, '[1,1,1]') as d from t1 order by d limit 10", + "explain select id, vec_l2_distance(vec, '[1,1,1]') as d from t1 order by d limit 10", + "explain select id, vec_negative_inner_product(vec, '[1,1,1]') as d from t1 order by d limit 10", + "explain select id, vec_dims(vec) as d from t1 order by d limit 10", + "explain select id, vec_l2_norm(vec) as d from t1 order by d limit 10", + "explain select id, MOD(a, 3) as d from t1 order by d limit 10", + + "explain select * from t1 order by vec_cosine_distance(vec, '[1,1,1]') limit 10", + "explain select * from t1 order by vec_l1_distance(vec, '[1,1,1]') limit 10", + "explain select * from t1 order by vec_l2_distance(vec, '[1,1,1]') limit 10", + "explain select * from t1 order by vec_negative_inner_product(vec, '[1,1,1]') limit 10", + "explain select * from t1 order by vec_dims(vec) limit 10", + "explain select * from t1 order by vec_l2_norm(vec) limit 10", + "explain select * from t1 order by MOD(a, 3) limit 10" + ] } ] diff --git a/pkg/planner/core/casetest/vectorsearch/testdata/ann_index_suite_out.json b/pkg/planner/core/casetest/vectorsearch/testdata/ann_index_suite_out.json index 232b1dc9eac01..933105120ae9e 100644 --- a/pkg/planner/core/casetest/vectorsearch/testdata/ann_index_suite_out.json +++ b/pkg/planner/core/casetest/vectorsearch/testdata/ann_index_suite_out.json @@ -67,156 +67,144 @@ { "SQL": "explain format = 'brief' select * from t1 order by vec_cosine_distance(vec, '[1,1,1]') limit 1", "Plan": [ - "Projection 1.00 root test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d", - "└─TopN 1.00 root Column#8, offset:0, count:1", - " └─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", - " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", - " └─TopN 1.00 mpp[tiflash] Column#8, offset:0, count:1", - " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#8", - " └─TableFullScan 1.00 mpp[tiflash] table:t1, index:vector_index(vec) keep order:false, stats:pseudo, annIndex:COSINE(vec..[1,1,1], limit:1)" + "TopN 1.00 root Column#8, offset:0, count:1", + "└─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", + " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN 1.00 mpp[tiflash] Column#8, offset:0, count:1", + " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#8", + " └─TableFullScan 1.00 mpp[tiflash] table:t1, index:vector_index(vec) keep order:false, stats:pseudo, annIndex:COSINE(vec..[1,1,1], limit:1)" ], "Warn": null }, { "SQL": "explain format = 'brief' select * from t1 order by vec_cosine_distance(vec, vec_from_text('[1,1,1]')) limit 1", "Plan": [ - "Projection 1.00 root test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d", - "└─TopN 1.00 root Column#8, offset:0, count:1", - " └─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", - " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", - " └─TopN 1.00 mpp[tiflash] Column#8, offset:0, count:1", - " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#8", - " └─TableFullScan 1.00 mpp[tiflash] table:t1, index:vector_index(vec) keep order:false, stats:pseudo, annIndex:COSINE(vec..[1,1,1], limit:1)" + "TopN 1.00 root Column#8, offset:0, count:1", + "└─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", + " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN 1.00 mpp[tiflash] Column#8, offset:0, count:1", + " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#8", + " └─TableFullScan 1.00 mpp[tiflash] table:t1, index:vector_index(vec) keep order:false, stats:pseudo, annIndex:COSINE(vec..[1,1,1], limit:1)" ], "Warn": null }, { "SQL": "explain format = 'brief' select * from t1 order by vec_cosine_distance(vec, '[1,1,1]') desc limit 1", "Plan": [ - "Projection 1.00 root test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d", - "└─TopN 1.00 root Column#7:desc, offset:0, count:1", - " └─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", - " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", - " └─TopN 1.00 mpp[tiflash] Column#7:desc, offset:0, count:1", - " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#7", - " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" + "TopN 1.00 root Column#7:desc, offset:0, count:1", + "└─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", + " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN 1.00 mpp[tiflash] Column#7:desc, offset:0, count:1", + " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#7", + " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" ], "Warn": null }, { "SQL": "explain format = 'brief' select * from t1 order by vec_cosine_distance(vec, vec_from_text('[1,1,1]')) desc limit 1", "Plan": [ - "Projection 1.00 root test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d", - "└─TopN 1.00 root Column#7:desc, offset:0, count:1", - " └─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", - " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", - " └─TopN 1.00 mpp[tiflash] Column#7:desc, offset:0, count:1", - " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#7", - " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" + "TopN 1.00 root Column#7:desc, offset:0, count:1", + "└─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", + " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN 1.00 mpp[tiflash] Column#7:desc, offset:0, count:1", + " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#7", + " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" ], "Warn": null }, { "SQL": "explain format = 'brief' select * from t1 order by vec_cosine_distance(vec, '[1,1,1]')+1 limit 1", "Plan": [ - "Projection 1.00 root test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d", - "└─TopN 1.00 root Column#7, offset:0, count:1", - " └─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", - " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", - " └─TopN 1.00 mpp[tiflash] Column#7, offset:0, count:1", - " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, plus(vec_cosine_distance(test.t1.vec, [1,1,1]), 1)->Column#7", - " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" + "TopN 1.00 root Column#7, offset:0, count:1", + "└─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", + " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN 1.00 mpp[tiflash] Column#7, offset:0, count:1", + " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, plus(vec_cosine_distance(test.t1.vec, [1,1,1]), 1)->Column#7", + " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" ], "Warn": null }, { "SQL": "explain format = 'brief' select * from t1 order by vec_cosine_distance(vec, '[1,1,1]'),vec limit 1", "Plan": [ - "Projection 1.00 root test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d", - "└─TopN 1.00 root Column#7, test.t1.vec, offset:0, count:1", - " └─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", - " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", - " └─TopN 1.00 mpp[tiflash] Column#7, test.t1.vec, offset:0, count:1", - " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#7", - " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" + "TopN 1.00 root Column#7, test.t1.vec, offset:0, count:1", + "└─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", + " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN 1.00 mpp[tiflash] Column#7, test.t1.vec, offset:0, count:1", + " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#7", + " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" ], "Warn": null }, { "SQL": "explain format = 'brief' select * from t1 order by vec_l2_distance(vec, '[1,1,1]') limit 1", "Plan": [ - "Projection 1.00 root test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d", - "└─TopN 1.00 root Column#7, offset:0, count:1", - " └─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", - " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", - " └─TopN 1.00 mpp[tiflash] Column#7, offset:0, count:1", - " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_l2_distance(test.t1.vec, [1,1,1])->Column#7", - " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" + "TopN 1.00 root Column#7, offset:0, count:1", + "└─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", + " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN 1.00 mpp[tiflash] Column#7, offset:0, count:1", + " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_l2_distance(test.t1.vec, [1,1,1])->Column#7", + " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" ], "Warn": null }, { "SQL": "explain format = 'brief' select * from t1 order by vec_l1_distance(vec, '[1,1,1]') limit 1", "Plan": [ - "Projection 1.00 root test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d", - "└─TopN 1.00 root Column#7, offset:0, count:1", - " └─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", - " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", - " └─TopN 1.00 mpp[tiflash] Column#7, offset:0, count:1", - " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_l1_distance(test.t1.vec, [1,1,1])->Column#7", - " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" + "TopN 1.00 root Column#7, offset:0, count:1", + "└─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", + " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN 1.00 mpp[tiflash] Column#7, offset:0, count:1", + " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_l1_distance(test.t1.vec, [1,1,1])->Column#7", + " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" ], "Warn": null }, { "SQL": "explain format = 'brief' select * from t1 order by vec_l2_distance(c, '[1,1,1]') limit 1", "Plan": [ - "Projection 1.00 root test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d", - "└─TopN 1.00 root Column#7, offset:0, count:1", - " └─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", - " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", - " └─TopN 1.00 mpp[tiflash] Column#7, offset:0, count:1", - " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_l2_distance(test.t1.c, [1,1,1])->Column#7", - " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" + "TopN 1.00 root Column#7, offset:0, count:1", + "└─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", + " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN 1.00 mpp[tiflash] Column#7, offset:0, count:1", + " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_l2_distance(test.t1.c, [1,1,1])->Column#7", + " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" ], "Warn": null }, { "SQL": "explain format = 'brief' select * from t1 order by vec_l2_distance(d, '[1,1,1]') limit 1", "Plan": [ - "Projection 1.00 root test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d", - "└─TopN 1.00 root Column#7, offset:0, count:1", - " └─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", - " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", - " └─TopN 1.00 mpp[tiflash] Column#7, offset:0, count:1", - " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_l2_distance(test.t1.d, [1,1,1])->Column#7", - " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" + "TopN 1.00 root Column#7, offset:0, count:1", + "└─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", + " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN 1.00 mpp[tiflash] Column#7, offset:0, count:1", + " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_l2_distance(test.t1.d, [1,1,1])->Column#7", + " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" ], "Warn": null }, { "SQL": "explain format = 'brief' select * from t1 order by vec_cosine_distance(d, '[1,1,1]') limit 1", "Plan": [ - "Projection 1.00 root test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d", - "└─TopN 1.00 root Column#7, offset:0, count:1", - " └─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", - " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", - " └─TopN 1.00 mpp[tiflash] Column#7, offset:0, count:1", - " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.d, [1,1,1])->Column#7", - " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" + "TopN 1.00 root Column#7, offset:0, count:1", + "└─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", + " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN 1.00 mpp[tiflash] Column#7, offset:0, count:1", + " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.d, [1,1,1])->Column#7", + " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" ], "Warn": null }, { "SQL": "explain format = 'brief' select * from t1 order by vec_l1_distance(d, '[1,1,1]') limit 1", "Plan": [ - "Projection 1.00 root test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d", - "└─TopN 1.00 root Column#7, offset:0, count:1", - " └─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", - " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", - " └─TopN 1.00 mpp[tiflash] Column#7, offset:0, count:1", - " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_l1_distance(test.t1.d, [1,1,1])->Column#7", - " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" + "TopN 1.00 root Column#7, offset:0, count:1", + "└─TableReader 1.00 root MppVersion: 2, data:ExchangeSender", + " └─ExchangeSender 1.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN 1.00 mpp[tiflash] Column#7, offset:0, count:1", + " └─Projection 1.00 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_l1_distance(test.t1.d, [1,1,1])->Column#7", + " └─TableFullScan 48.00 mpp[tiflash] table:t1 keep order:false, stats:pseudo" ], "Warn": null }, @@ -260,14 +248,13 @@ { "SQL": "explain format = 'brief' select * from t1 where a=0 order by vec_cosine_distance(vec, '[1,1,1]') limit 1", "Plan": [ - "Projection 0.05 root test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d", - "└─TopN 0.05 root Column#7, offset:0, count:1", - " └─TableReader 0.05 root MppVersion: 2, data:ExchangeSender", - " └─ExchangeSender 0.05 mpp[tiflash] ExchangeType: PassThrough", - " └─TopN 0.05 mpp[tiflash] Column#7, offset:0, count:1", - " └─Projection 0.05 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#7", - " └─Selection 0.05 mpp[tiflash] eq(test.t1.a, 0)", - " └─TableFullScan 48.00 mpp[tiflash] table:t1 pushed down filter:empty, keep order:false, stats:pseudo" + "TopN 0.05 root Column#7, offset:0, count:1", + "└─TableReader 0.05 root MppVersion: 2, data:ExchangeSender", + " └─ExchangeSender 0.05 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN 0.05 mpp[tiflash] Column#7, offset:0, count:1", + " └─Projection 0.05 mpp[tiflash] test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#7", + " └─Selection 0.05 mpp[tiflash] eq(test.t1.a, 0)", + " └─TableFullScan 48.00 mpp[tiflash] table:t1 pushed down filter:empty, keep order:false, stats:pseudo" ], "Warn": null } @@ -460,13 +447,12 @@ { "SQL": "explain select id from t1 order by vec_cosine_distance(vec, '[1,1,1]') limit 10", "Plan": [ - "Projection_7 10.00 root test.t1.id", - "└─TopN_11 10.00 root Column#10, offset:0, count:10", - " └─TableReader_29 10.00 root MppVersion: 2, data:ExchangeSender_28", - " └─ExchangeSender_28 10.00 mpp[tiflash] ExchangeType: PassThrough", - " └─TopN_27 10.00 mpp[tiflash] Column#10, offset:0, count:10", - " └─Projection_26 10.00 mpp[tiflash] test.t1.id, test.t1.vec, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#10", - " └─TableFullScan_25 10.00 mpp[tiflash] table:t1, index:idx_embedding(vec) keep order:false, annIndex:COSINE(vec..[1,1,1], limit:10)" + "TopN_11 10.00 root Column#10, offset:0, count:10", + "└─TableReader_29 10.00 root MppVersion: 2, data:ExchangeSender_28", + " └─ExchangeSender_28 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_27 10.00 mpp[tiflash] Column#10, offset:0, count:10", + " └─Projection_26 10.00 mpp[tiflash] test.t1.id, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#10", + " └─TableFullScan_25 10.00 mpp[tiflash] table:t1, index:idx_embedding(vec) keep order:false, annIndex:COSINE(vec..[1,1,1], limit:10)" ], "Warn": null }, @@ -486,26 +472,24 @@ { "SQL": "explain select * from t1 order by vec_cosine_distance(vec, '[1,1,1]') limit 10", "Plan": [ - "Projection_6 10.00 root test.t1.id, test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d", - "└─TopN_10 10.00 root Column#9, offset:0, count:10", - " └─TableReader_28 10.00 root MppVersion: 2, data:ExchangeSender_27", - " └─ExchangeSender_27 10.00 mpp[tiflash] ExchangeType: PassThrough", - " └─TopN_26 10.00 mpp[tiflash] Column#9, offset:0, count:10", - " └─Projection_25 10.00 mpp[tiflash] test.t1.id, test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#9", - " └─TableFullScan_24 10.00 mpp[tiflash] table:t1, index:idx_embedding(vec) keep order:false, annIndex:COSINE(vec..[1,1,1], limit:10)" + "TopN_10 10.00 root Column#9, offset:0, count:10", + "└─TableReader_28 10.00 root MppVersion: 2, data:ExchangeSender_27", + " └─ExchangeSender_27 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_26 10.00 mpp[tiflash] Column#9, offset:0, count:10", + " └─Projection_25 10.00 mpp[tiflash] test.t1.id, test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#9", + " └─TableFullScan_24 10.00 mpp[tiflash] table:t1, index:idx_embedding(vec) keep order:false, annIndex:COSINE(vec..[1,1,1], limit:10)" ], "Warn": null }, { "SQL": "explain select id, a, b from t1 order by vec_cosine_distance(vec, '[1,1,1]') limit 10", "Plan": [ - "Projection_7 10.00 root test.t1.id, test.t1.a, test.t1.b", - "└─TopN_11 10.00 root Column#12, offset:0, count:10", - " └─TableReader_29 10.00 root MppVersion: 2, data:ExchangeSender_28", - " └─ExchangeSender_28 10.00 mpp[tiflash] ExchangeType: PassThrough", - " └─TopN_27 10.00 mpp[tiflash] Column#12, offset:0, count:10", - " └─Projection_26 10.00 mpp[tiflash] test.t1.id, test.t1.vec, test.t1.a, test.t1.b, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#12", - " └─TableFullScan_25 10.00 mpp[tiflash] table:t1, index:idx_embedding(vec) keep order:false, annIndex:COSINE(vec..[1,1,1], limit:10)" + "TopN_11 10.00 root Column#12, offset:0, count:10", + "└─TableReader_29 10.00 root MppVersion: 2, data:ExchangeSender_28", + " └─ExchangeSender_28 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_27 10.00 mpp[tiflash] Column#12, offset:0, count:10", + " └─Projection_26 10.00 mpp[tiflash] test.t1.id, test.t1.a, test.t1.b, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#12", + " └─TableFullScan_25 10.00 mpp[tiflash] table:t1, index:idx_embedding(vec) keep order:false, annIndex:COSINE(vec..[1,1,1], limit:10)" ], "Warn": null }, @@ -517,7 +501,7 @@ " └─TableReader_29 10.00 root MppVersion: 2, data:ExchangeSender_28", " └─ExchangeSender_28 10.00 mpp[tiflash] ExchangeType: PassThrough", " └─TopN_27 10.00 mpp[tiflash] Column#12, offset:0, count:10", - " └─Projection_26 10.00 mpp[tiflash] test.t1.id, test.t1.vec, test.t1.a, test.t1.b, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#12", + " └─Projection_26 10.00 mpp[tiflash] test.t1.id, test.t1.a, test.t1.b, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#12", " └─TableFullScan_25 10.00 mpp[tiflash] table:t1, index:idx_embedding(vec) keep order:false, annIndex:COSINE(vec..[1,1,1], limit:10)" ], "Warn": null @@ -601,12 +585,11 @@ { "SQL": "explain select id from t1 order by vec_cosine_distance(vec, '[1,1,1]') limit 10", "Plan": [ - "Projection_7 10.00 root test.t1.id", - "└─TopN_8 10.00 root Column#8, offset:0, count:10", - " └─TableReader_15 10.00 root data:TopN_14", - " └─TopN_14 10.00 cop[tikv] Column#8, offset:0, count:10", - " └─Projection_13 10.00 cop[tikv] test.t1.id, test.t1.vec, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#8", - " └─TableFullScan_12 6000.00 cop[tikv] table:t1 keep order:false" + "TopN_8 10.00 root Column#8, offset:0, count:10", + "└─TableReader_15 10.00 root data:TopN_14", + " └─TopN_14 10.00 cop[tikv] Column#8, offset:0, count:10", + " └─Projection_13 10.00 cop[tikv] test.t1.id, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#8", + " └─TableFullScan_12 6000.00 cop[tikv] table:t1 keep order:false" ], "Warn": null }, @@ -625,24 +608,22 @@ { "SQL": "explain select * from t1 order by vec_cosine_distance(vec, '[1,1,1]') limit 10", "Plan": [ - "Projection_6 10.00 root test.t1.id, test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d", - "└─TopN_7 10.00 root Column#7, offset:0, count:10", - " └─TableReader_14 10.00 root data:TopN_13", - " └─TopN_13 10.00 cop[tikv] Column#7, offset:0, count:10", - " └─Projection_12 10.00 cop[tikv] test.t1.id, test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#7", - " └─TableFullScan_11 6000.00 cop[tikv] table:t1 keep order:false" + "TopN_7 10.00 root Column#7, offset:0, count:10", + "└─TableReader_14 10.00 root data:TopN_13", + " └─TopN_13 10.00 cop[tikv] Column#7, offset:0, count:10", + " └─Projection_12 10.00 cop[tikv] test.t1.id, test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#7", + " └─TableFullScan_11 6000.00 cop[tikv] table:t1 keep order:false" ], "Warn": null }, { "SQL": "explain select id, a, b from t1 order by vec_cosine_distance(vec, '[1,1,1]') limit 10", "Plan": [ - "Projection_7 10.00 root test.t1.id, test.t1.a, test.t1.b", - "└─TopN_8 10.00 root Column#10, offset:0, count:10", - " └─TableReader_15 10.00 root data:TopN_14", - " └─TopN_14 10.00 cop[tikv] Column#10, offset:0, count:10", - " └─Projection_13 10.00 cop[tikv] test.t1.id, test.t1.vec, test.t1.a, test.t1.b, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#10", - " └─TableFullScan_12 6000.00 cop[tikv] table:t1 keep order:false" + "TopN_8 10.00 root Column#10, offset:0, count:10", + "└─TableReader_15 10.00 root data:TopN_14", + " └─TopN_14 10.00 cop[tikv] Column#10, offset:0, count:10", + " └─Projection_13 10.00 cop[tikv] test.t1.id, test.t1.a, test.t1.b, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#10", + " └─TableFullScan_12 6000.00 cop[tikv] table:t1 keep order:false" ], "Warn": null }, @@ -653,7 +634,7 @@ "└─TopN_8 10.00 root Column#10, offset:0, count:10", " └─TableReader_15 10.00 root data:TopN_14", " └─TopN_14 10.00 cop[tikv] Column#10, offset:0, count:10", - " └─Projection_13 10.00 cop[tikv] test.t1.id, test.t1.vec, test.t1.a, test.t1.b, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#10", + " └─Projection_13 10.00 cop[tikv] test.t1.id, test.t1.a, test.t1.b, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#10", " └─TableFullScan_12 6000.00 cop[tikv] table:t1 keep order:false" ], "Warn": null @@ -717,5 +698,278 @@ "Warn": null } ] + }, + { + "Name": "TestVectorSearchHeavyFunction", + "Cases": [ + { + "SQL": "explain select id from t1 order by vec_cosine_distance(vec, '[1,1,1]') limit 10", + "Plan": [ + "TopN_11 10.00 root Column#10, offset:0, count:10", + "└─TableReader_29 10.00 root MppVersion: 2, data:ExchangeSender_28", + " └─ExchangeSender_28 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_27 10.00 mpp[tiflash] Column#10, offset:0, count:10", + " └─Projection_26 10.00 mpp[tiflash] test.t1.id, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#10", + " └─TableFullScan_25 10.00 mpp[tiflash] table:t1, index:idx_embedding(vec) keep order:false, annIndex:COSINE(vec..[1,1,1], limit:10)" + ], + "Warn": null + }, + { + "SQL": "explain select id from t1 order by vec_l1_distance(vec, '[1,1,1]') limit 10", + "Plan": [ + "TopN_10 10.00 root Column#9, offset:0, count:10", + "└─TableReader_23 10.00 root MppVersion: 2, data:ExchangeSender_22", + " └─ExchangeSender_22 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_21 10.00 mpp[tiflash] Column#9, offset:0, count:10", + " └─Projection_20 10.00 mpp[tiflash] test.t1.id, vec_l1_distance(test.t1.vec, [1,1,1])->Column#9", + " └─TableFullScan_18 6000.00 mpp[tiflash] table:t1 keep order:false" + ], + "Warn": null + }, + { + "SQL": "explain select id from t1 order by vec_l2_distance(vec, '[1,1,1]') limit 10", + "Plan": [ + "TopN_10 10.00 root Column#9, offset:0, count:10", + "└─TableReader_23 10.00 root MppVersion: 2, data:ExchangeSender_22", + " └─ExchangeSender_22 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_21 10.00 mpp[tiflash] Column#9, offset:0, count:10", + " └─Projection_20 10.00 mpp[tiflash] test.t1.id, vec_l2_distance(test.t1.vec, [1,1,1])->Column#9", + " └─TableFullScan_18 6000.00 mpp[tiflash] table:t1 keep order:false" + ], + "Warn": null + }, + { + "SQL": "explain select id from t1 order by vec_negative_inner_product(vec, '[1,1,1]') limit 10", + "Plan": [ + "TopN_10 10.00 root Column#9, offset:0, count:10", + "└─TableReader_23 10.00 root MppVersion: 2, data:ExchangeSender_22", + " └─ExchangeSender_22 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_21 10.00 mpp[tiflash] Column#9, offset:0, count:10", + " └─Projection_20 10.00 mpp[tiflash] test.t1.id, vec_negative_inner_product(test.t1.vec, [1,1,1])->Column#9", + " └─TableFullScan_18 6000.00 mpp[tiflash] table:t1 keep order:false" + ], + "Warn": null + }, + { + "SQL": "explain select id from t1 order by vec_dims(vec) limit 10", + "Plan": [ + "TopN_10 10.00 root Column#9, offset:0, count:10", + "└─TableReader_22 10.00 root MppVersion: 2, data:ExchangeSender_21", + " └─ExchangeSender_21 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_20 10.00 mpp[tiflash] Column#9, offset:0, count:10", + " └─Projection_19 10.00 mpp[tiflash] test.t1.id, vec_dims(test.t1.vec)->Column#9", + " └─TableFullScan_17 6000.00 mpp[tiflash] table:t1 keep order:false" + ], + "Warn": null + }, + { + "SQL": "explain select id from t1 order by vec_l2_norm(vec) limit 10", + "Plan": [ + "TopN_10 10.00 root Column#9, offset:0, count:10", + "└─TableReader_22 10.00 root MppVersion: 2, data:ExchangeSender_21", + " └─ExchangeSender_21 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_20 10.00 mpp[tiflash] Column#9, offset:0, count:10", + " └─Projection_19 10.00 mpp[tiflash] test.t1.id, vec_l2_norm(test.t1.vec)->Column#9", + " └─TableFullScan_17 6000.00 mpp[tiflash] table:t1 keep order:false" + ], + "Warn": null + }, + { + "SQL": "explain select id from t1 order by MOD(a, 3) limit 10", + "Plan": [ + "Projection_23 10.00 root test.t1.id", + "└─TopN_10 10.00 root Column#9, offset:0, count:10", + " └─Projection_24 10.00 root test.t1.id, test.t1.a, mod(test.t1.a, 3)->Column#9", + " └─TableReader_20 10.00 root MppVersion: 2, data:ExchangeSender_19", + " └─ExchangeSender_19 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─Projection_21 10.00 mpp[tiflash] test.t1.id, test.t1.a", + " └─TopN_18 10.00 mpp[tiflash] Column#8, offset:0, count:10", + " └─Projection_22 6000.00 mpp[tiflash] test.t1.id, test.t1.a, mod(test.t1.a, 3)->Column#8", + " └─TableFullScan_16 6000.00 mpp[tiflash] table:t1 keep order:false" + ], + "Warn": null + }, + { + "SQL": "explain select id, vec_cosine_distance(vec, '[1,1,1]') as d from t1 order by d limit 10", + "Plan": [ + "Projection_6 10.00 root test.t1.id, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#7", + "└─TopN_10 10.00 root Column#10, offset:0, count:10", + " └─TableReader_28 10.00 root MppVersion: 2, data:ExchangeSender_27", + " └─ExchangeSender_27 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_26 10.00 mpp[tiflash] Column#10, offset:0, count:10", + " └─Projection_25 10.00 mpp[tiflash] test.t1.id, test.t1.vec, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#10", + " └─TableFullScan_24 10.00 mpp[tiflash] table:t1, index:idx_embedding(vec) keep order:false, annIndex:COSINE(vec..[1,1,1], limit:10)" + ], + "Warn": null + }, + { + "SQL": "explain select id, vec_l1_distance(vec, '[1,1,1]') as d from t1 order by d limit 10", + "Plan": [ + "Projection_6 10.00 root test.t1.id, vec_l1_distance(test.t1.vec, [1,1,1])->Column#7", + "└─TopN_9 10.00 root Column#9, offset:0, count:10", + " └─TableReader_22 10.00 root MppVersion: 2, data:ExchangeSender_21", + " └─ExchangeSender_21 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_20 10.00 mpp[tiflash] Column#9, offset:0, count:10", + " └─Projection_19 10.00 mpp[tiflash] test.t1.id, test.t1.vec, vec_l1_distance(test.t1.vec, [1,1,1])->Column#9", + " └─TableFullScan_17 6000.00 mpp[tiflash] table:t1 keep order:false" + ], + "Warn": null + }, + { + "SQL": "explain select id, vec_l2_distance(vec, '[1,1,1]') as d from t1 order by d limit 10", + "Plan": [ + "Projection_6 10.00 root test.t1.id, vec_l2_distance(test.t1.vec, [1,1,1])->Column#7", + "└─TopN_9 10.00 root Column#9, offset:0, count:10", + " └─TableReader_22 10.00 root MppVersion: 2, data:ExchangeSender_21", + " └─ExchangeSender_21 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_20 10.00 mpp[tiflash] Column#9, offset:0, count:10", + " └─Projection_19 10.00 mpp[tiflash] test.t1.id, test.t1.vec, vec_l2_distance(test.t1.vec, [1,1,1])->Column#9", + " └─TableFullScan_17 6000.00 mpp[tiflash] table:t1 keep order:false" + ], + "Warn": null + }, + { + "SQL": "explain select id, vec_negative_inner_product(vec, '[1,1,1]') as d from t1 order by d limit 10", + "Plan": [ + "Projection_6 10.00 root test.t1.id, vec_negative_inner_product(test.t1.vec, [1,1,1])->Column#7", + "└─TopN_9 10.00 root Column#9, offset:0, count:10", + " └─TableReader_22 10.00 root MppVersion: 2, data:ExchangeSender_21", + " └─ExchangeSender_21 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_20 10.00 mpp[tiflash] Column#9, offset:0, count:10", + " └─Projection_19 10.00 mpp[tiflash] test.t1.id, test.t1.vec, vec_negative_inner_product(test.t1.vec, [1,1,1])->Column#9", + " └─TableFullScan_17 6000.00 mpp[tiflash] table:t1 keep order:false" + ], + "Warn": null + }, + { + "SQL": "explain select id, vec_dims(vec) as d from t1 order by d limit 10", + "Plan": [ + "Projection_6 10.00 root test.t1.id, vec_dims(test.t1.vec)->Column#7", + "└─TopN_9 10.00 root Column#9, offset:0, count:10", + " └─TableReader_21 10.00 root MppVersion: 2, data:ExchangeSender_20", + " └─ExchangeSender_20 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_19 10.00 mpp[tiflash] Column#9, offset:0, count:10", + " └─Projection_18 10.00 mpp[tiflash] test.t1.id, test.t1.vec, vec_dims(test.t1.vec)->Column#9", + " └─TableFullScan_16 6000.00 mpp[tiflash] table:t1 keep order:false" + ], + "Warn": null + }, + { + "SQL": "explain select id, vec_l2_norm(vec) as d from t1 order by d limit 10", + "Plan": [ + "Projection_6 10.00 root test.t1.id, vec_l2_norm(test.t1.vec)->Column#7", + "└─TopN_9 10.00 root Column#9, offset:0, count:10", + " └─TableReader_21 10.00 root MppVersion: 2, data:ExchangeSender_20", + " └─ExchangeSender_20 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_19 10.00 mpp[tiflash] Column#9, offset:0, count:10", + " └─Projection_18 10.00 mpp[tiflash] test.t1.id, test.t1.vec, vec_l2_norm(test.t1.vec)->Column#9", + " └─TableFullScan_16 6000.00 mpp[tiflash] table:t1 keep order:false" + ], + "Warn": null + }, + { + "SQL": "explain select id, MOD(a, 3) as d from t1 order by d limit 10", + "Plan": [ + "Projection_6 10.00 root test.t1.id, mod(test.t1.a, 3)->Column#7", + "└─Projection_22 10.00 root test.t1.id, test.t1.a", + " └─TopN_9 10.00 root Column#9, offset:0, count:10", + " └─Projection_23 10.00 root test.t1.id, test.t1.a, mod(test.t1.a, 3)->Column#9", + " └─TableReader_19 10.00 root MppVersion: 2, data:ExchangeSender_18", + " └─ExchangeSender_18 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─Projection_20 10.00 mpp[tiflash] test.t1.id, test.t1.a", + " └─TopN_17 10.00 mpp[tiflash] Column#8, offset:0, count:10", + " └─Projection_21 6000.00 mpp[tiflash] test.t1.id, test.t1.a, mod(test.t1.a, 3)->Column#8", + " └─TableFullScan_15 6000.00 mpp[tiflash] table:t1 keep order:false" + ], + "Warn": null + }, + { + "SQL": "explain select * from t1 order by vec_cosine_distance(vec, '[1,1,1]') limit 10", + "Plan": [ + "TopN_10 10.00 root Column#9, offset:0, count:10", + "└─TableReader_28 10.00 root MppVersion: 2, data:ExchangeSender_27", + " └─ExchangeSender_27 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_26 10.00 mpp[tiflash] Column#9, offset:0, count:10", + " └─Projection_25 10.00 mpp[tiflash] test.t1.id, test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_cosine_distance(test.t1.vec, [1,1,1])->Column#9", + " └─TableFullScan_24 10.00 mpp[tiflash] table:t1, index:idx_embedding(vec) keep order:false, annIndex:COSINE(vec..[1,1,1], limit:10)" + ], + "Warn": null + }, + { + "SQL": "explain select * from t1 order by vec_l1_distance(vec, '[1,1,1]') limit 10", + "Plan": [ + "TopN_9 10.00 root Column#8, offset:0, count:10", + "└─TableReader_22 10.00 root MppVersion: 2, data:ExchangeSender_21", + " └─ExchangeSender_21 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_20 10.00 mpp[tiflash] Column#8, offset:0, count:10", + " └─Projection_19 10.00 mpp[tiflash] test.t1.id, test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_l1_distance(test.t1.vec, [1,1,1])->Column#8", + " └─TableFullScan_17 6000.00 mpp[tiflash] table:t1 keep order:false" + ], + "Warn": null + }, + { + "SQL": "explain select * from t1 order by vec_l2_distance(vec, '[1,1,1]') limit 10", + "Plan": [ + "TopN_9 10.00 root Column#8, offset:0, count:10", + "└─TableReader_22 10.00 root MppVersion: 2, data:ExchangeSender_21", + " └─ExchangeSender_21 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_20 10.00 mpp[tiflash] Column#8, offset:0, count:10", + " └─Projection_19 10.00 mpp[tiflash] test.t1.id, test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_l2_distance(test.t1.vec, [1,1,1])->Column#8", + " └─TableFullScan_17 6000.00 mpp[tiflash] table:t1 keep order:false" + ], + "Warn": null + }, + { + "SQL": "explain select * from t1 order by vec_negative_inner_product(vec, '[1,1,1]') limit 10", + "Plan": [ + "TopN_9 10.00 root Column#8, offset:0, count:10", + "└─TableReader_22 10.00 root MppVersion: 2, data:ExchangeSender_21", + " └─ExchangeSender_21 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_20 10.00 mpp[tiflash] Column#8, offset:0, count:10", + " └─Projection_19 10.00 mpp[tiflash] test.t1.id, test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_negative_inner_product(test.t1.vec, [1,1,1])->Column#8", + " └─TableFullScan_17 6000.00 mpp[tiflash] table:t1 keep order:false" + ], + "Warn": null + }, + { + "SQL": "explain select * from t1 order by vec_dims(vec) limit 10", + "Plan": [ + "TopN_9 10.00 root Column#8, offset:0, count:10", + "└─TableReader_21 10.00 root MppVersion: 2, data:ExchangeSender_20", + " └─ExchangeSender_20 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_19 10.00 mpp[tiflash] Column#8, offset:0, count:10", + " └─Projection_18 10.00 mpp[tiflash] test.t1.id, test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_dims(test.t1.vec)->Column#8", + " └─TableFullScan_16 6000.00 mpp[tiflash] table:t1 keep order:false" + ], + "Warn": null + }, + { + "SQL": "explain select * from t1 order by vec_l2_norm(vec) limit 10", + "Plan": [ + "TopN_9 10.00 root Column#8, offset:0, count:10", + "└─TableReader_21 10.00 root MppVersion: 2, data:ExchangeSender_20", + " └─ExchangeSender_20 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TopN_19 10.00 mpp[tiflash] Column#8, offset:0, count:10", + " └─Projection_18 10.00 mpp[tiflash] test.t1.id, test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, vec_l2_norm(test.t1.vec)->Column#8", + " └─TableFullScan_16 6000.00 mpp[tiflash] table:t1 keep order:false" + ], + "Warn": null + }, + { + "SQL": "explain select * from t1 order by MOD(a, 3) limit 10", + "Plan": [ + "Projection_22 10.00 root test.t1.id, test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d", + "└─TopN_9 10.00 root Column#8, offset:0, count:10", + " └─Projection_23 10.00 root test.t1.id, test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, mod(test.t1.a, 3)->Column#8", + " └─TableReader_19 10.00 root MppVersion: 2, data:ExchangeSender_18", + " └─ExchangeSender_18 10.00 mpp[tiflash] ExchangeType: PassThrough", + " └─Projection_20 10.00 mpp[tiflash] test.t1.id, test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d", + " └─TopN_17 10.00 mpp[tiflash] Column#7, offset:0, count:10", + " └─Projection_21 6000.00 mpp[tiflash] test.t1.id, test.t1.vec, test.t1.a, test.t1.b, test.t1.c, test.t1.d, mod(test.t1.a, 3)->Column#7", + " └─TableFullScan_15 6000.00 mpp[tiflash] table:t1 keep order:false" + ], + "Warn": null + } + ] } ] \ No newline at end of file diff --git a/pkg/planner/core/casetest/vectorsearch/vector_index_test.go b/pkg/planner/core/casetest/vectorsearch/vector_index_test.go index 1f1129bd3f81e..5efd71b8dca76 100644 --- a/pkg/planner/core/casetest/vectorsearch/vector_index_test.go +++ b/pkg/planner/core/casetest/vectorsearch/vector_index_test.go @@ -176,13 +176,12 @@ func TestANNIndexNormalizedPlan(t *testing.T) { tk.MustExec("explain select * from t order by vec_cosine_distance(vec, '[0,0,0]') limit 1") p1, d1 := getNormalizedPlan() require.Equal(t, []string{ - " Projection root test.t.vec", - " └─TopN root ?", - " └─TableReader root ", - " └─ExchangeSender cop[tiflash] ", - " └─TopN cop[tiflash] ?", - " └─Projection cop[tiflash] test.t.vec, vec_cosine_distance(test.t.vec, ?)", - " └─TableFullScan cop[tiflash] table:t, index:vector_index(vec), range:[?,?], keep order:false, annIndex:COSINE(vec..[?], limit:?)", + " TopN root ?", + " └─TableReader root ", + " └─ExchangeSender cop[tiflash] ", + " └─TopN cop[tiflash] ?", + " └─Projection cop[tiflash] test.t.vec, vec_cosine_distance(test.t.vec, ?)", + " └─TableFullScan cop[tiflash] table:t, index:vector_index(vec), range:[?,?], keep order:false, annIndex:COSINE(vec..[?], limit:?)", }, p1) tk.MustExec("explain select * from t order by vec_cosine_distance(vec, '[1,2,3]') limit 3") @@ -206,12 +205,11 @@ func TestANNIndexNormalizedPlan(t *testing.T) { tk.MustExec("explain select * from t order by vec_cosine_distance(vec, '[1,2,3]') limit 3") p2, _ := getNormalizedPlan() require.Equal(t, []string{ - " Projection root test.t.vec", - " └─TopN root ?", - " └─TableReader root ", - " └─TopN cop ?", - " └─Projection cop test.t.vec, vec_cosine_distance(test.t.vec, ?)", - " └─TableFullScan cop table:t, range:[?,?], keep order:false", + " TopN root ?", + " └─TableReader root ", + " └─TopN cop ?", + " └─Projection cop test.t.vec, vec_cosine_distance(test.t.vec, ?)", + " └─TableFullScan cop table:t, range:[?,?], keep order:false", }, p2) tbl.Meta().TiFlashReplica.Available = true tk.MustExec("explain select * from t order by vec_cosine_distance(vec, '[1,2,3]') limit 3") @@ -413,3 +411,32 @@ func TestVectorSearchWithPKForceTiKV(t *testing.T) { require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) } } + +func TestVectorSearchHeavyFunction(t *testing.T) { + tk := prepareVectorSearchWithPK(t) + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + suiteData := GetANNIndexSuiteData() + suiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { + tk.MustExec(tt) + continue + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } +} diff --git a/pkg/planner/core/exhaust_physical_plans.go b/pkg/planner/core/exhaust_physical_plans.go index 3c47bcce17fab..3d0e4088e2a44 100644 --- a/pkg/planner/core/exhaust_physical_plans.go +++ b/pkg/planner/core/exhaust_physical_plans.go @@ -2251,6 +2251,7 @@ func getPhysTopN(lt *logicalop.LogicalTopN, prop *property.PhysicalProperty) []b Count: lt.Count, Offset: lt.Offset, }.Init(lt.SCtx(), lt.StatsInfo(), lt.QueryBlockOffset(), resultProp) + topN.SetSchema(lt.Schema()) ret = append(ret, topN) } // If we can generate MPP task and there's vector distance function in the order by column. @@ -2289,6 +2290,7 @@ func getPhysTopN(lt *logicalop.LogicalTopN, prop *property.PhysicalProperty) []b Count: lt.Count, Offset: lt.Offset, }.Init(lt.SCtx(), lt.StatsInfo(), lt.QueryBlockOffset(), resultProp) + topN.SetSchema(lt.Schema()) ret = append(ret, topN) } return ret diff --git a/pkg/planner/core/operator/logicalop/hash64_equals_generated.go b/pkg/planner/core/operator/logicalop/hash64_equals_generated.go index 74056cc19b3c7..62b3c27bc6e05 100644 --- a/pkg/planner/core/operator/logicalop/hash64_equals_generated.go +++ b/pkg/planner/core/operator/logicalop/hash64_equals_generated.go @@ -868,6 +868,7 @@ func (op *LogicalTableDual) Equals(other any) bool { // Hash64 implements the Hash64Equals interface. func (op *LogicalTopN) Hash64(h base.Hasher) { h.HashString(plancodec.TypeTopN) + op.LogicalSchemaProducer.Hash64(h) if op.ByItems == nil { h.HashByte(base.NilFlag) } else { @@ -903,6 +904,9 @@ func (op *LogicalTopN) Equals(other any) bool { if op2 == nil { return false } + if !op.LogicalSchemaProducer.Equals(&op2.LogicalSchemaProducer) { + return false + } if (op.ByItems == nil && op2.ByItems != nil) || (op.ByItems != nil && op2.ByItems == nil) || len(op.ByItems) != len(op2.ByItems) { return false } diff --git a/pkg/planner/core/operator/logicalop/logical_top_n.go b/pkg/planner/core/operator/logicalop/logical_top_n.go index 8a67911a434d0..303b40f363dcd 100644 --- a/pkg/planner/core/operator/logicalop/logical_top_n.go +++ b/pkg/planner/core/operator/logicalop/logical_top_n.go @@ -30,7 +30,7 @@ import ( // LogicalTopN represents a top-n plan. type LogicalTopN struct { - BaseLogicalPlan + LogicalSchemaProducer `hash64-equals:"true"` ByItems []*util.ByItems `hash64-equals:"true"` // PartitionBy is used for extended TopN to consider K heaps. Used by rule_derive_topn_from_window @@ -82,6 +82,9 @@ func (lt *LogicalTopN) ReplaceExprColumns(replace map[string]*expression.Column) func (lt *LogicalTopN) PruneColumns(parentUsedCols []*expression.Column, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, error) { child := lt.Children()[0] var cols []*expression.Column + + lt.InlineProjection(parentUsedCols, opt) + lt.ByItems, cols = pruneByItems(lt, lt.ByItems, opt) parentUsedCols = append(parentUsedCols, cols...) var err error @@ -96,7 +99,7 @@ func (lt *LogicalTopN) PruneColumns(parentUsedCols []*expression.Column, opt *op // BuildKeyInfo implements base.LogicalPlan.<4th> interface. func (lt *LogicalTopN) BuildKeyInfo(selfSchema *expression.Schema, childSchema []*expression.Schema) { - lt.BaseLogicalPlan.BuildKeyInfo(selfSchema, childSchema) + lt.LogicalSchemaProducer.BuildKeyInfo(selfSchema, childSchema) if lt.Count == 1 { lt.SetMaxOneRow(true) } diff --git a/pkg/planner/core/physical_plans.go b/pkg/planner/core/physical_plans.go index 490aaba00f386..40feb3d27d629 100644 --- a/pkg/planner/core/physical_plans.go +++ b/pkg/planner/core/physical_plans.go @@ -1196,7 +1196,7 @@ func (p *PhysicalProjection) MemoryUsage() (sum int64) { // PhysicalTopN is the physical operator of topN. type PhysicalTopN struct { - physicalop.BasePhysicalPlan + physicalSchemaProducer ByItems []*util.ByItems PartitionBy []property.SortItem @@ -1214,11 +1214,11 @@ func (lt *PhysicalTopN) Clone(newCtx base.PlanContext) (base.PhysicalPlan, error cloned := new(PhysicalTopN) *cloned = *lt cloned.SetSCtx(newCtx) - base, err := lt.BasePhysicalPlan.CloneWithSelf(newCtx, cloned) + base, err := lt.physicalSchemaProducer.cloneWithSelf(newCtx, cloned) if err != nil { return nil, err } - cloned.BasePhysicalPlan = *base + cloned.physicalSchemaProducer = *base cloned.ByItems = make([]*util.ByItems, 0, len(lt.ByItems)) for _, it := range lt.ByItems { cloned.ByItems = append(cloned.ByItems, it.Clone()) diff --git a/pkg/planner/core/plan_clone_generated.go b/pkg/planner/core/plan_clone_generated.go index 7bfbc0eec1618..69cdcfe24c8c3 100644 --- a/pkg/planner/core/plan_clone_generated.go +++ b/pkg/planner/core/plan_clone_generated.go @@ -127,11 +127,11 @@ func (op *PhysicalSort) CloneForPlanCache(newCtx base.PlanContext) (base.Plan, b func (op *PhysicalTopN) CloneForPlanCache(newCtx base.PlanContext) (base.Plan, bool) { cloned := new(PhysicalTopN) *cloned = *op - basePlan, baseOK := op.BasePhysicalPlan.CloneForPlanCacheWithSelf(newCtx, cloned) + basePlan, baseOK := op.physicalSchemaProducer.cloneForPlanCacheWithSelf(newCtx, cloned) if !baseOK { return nil, false } - cloned.BasePhysicalPlan = *basePlan + cloned.physicalSchemaProducer = *basePlan cloned.ByItems = util.CloneByItemss(op.ByItems) cloned.PartitionBy = util.CloneSortItems(op.PartitionBy) return cloned, true diff --git a/pkg/planner/core/resolve_indices.go b/pkg/planner/core/resolve_indices.go index a930a7b31a0a2..fde583a164838 100644 --- a/pkg/planner/core/resolve_indices.go +++ b/pkg/planner/core/resolve_indices.go @@ -698,9 +698,39 @@ func (p *PhysicalShuffle) ResolveIndices() (err error) { return err } +// resolveIndexForInlineProjection ensures that during the execution of the physical plan, the column index can +// be correctly mapped to the column in its subplan. +func resolveIndexForInlineProjection(p *physicalSchemaProducer) error { + // To avoid that two plan shares the same column slice. + shallowColSlice := make([]*expression.Column, p.Schema().Len()) + copy(shallowColSlice, p.Schema().Columns) + p.schema = expression.NewSchema(shallowColSlice...) + foundCnt := 0 + // The two column sets are all ordered. And the colsNeedResolving is the subset of the mergedSchema. + // So we can just move forward j if there's no matching is found. + // We don't use the normal ResolvIndices here since there might be duplicate columns in the schema. + // e.g. The schema of child_0 is [col0, col0, col1] + // ResolveIndices will only resolve all col0 reference of the current plan to the first col0. + for i, j := 0, 0; i < p.Schema().Len() && j < p.Children()[0].Schema().Len(); { + if !p.Schema().Columns[i].Equal(nil, p.Children()[0].Schema().Columns[j]) { + j++ + continue + } + p.Schema().Columns[i] = p.schema.Columns[i].Clone().(*expression.Column) + p.Schema().Columns[i].Index = j + i++ + j++ + foundCnt++ + } + if foundCnt < p.Schema().Len() { + return errors.Errorf("Some columns of %v cannot find the reference from its child(ren)", p.ExplainID().String()) + } + return nil +} + // ResolveIndices implements Plan interface. func (p *PhysicalTopN) ResolveIndices() (err error) { - err = p.BasePhysicalPlan.ResolveIndices() + err = p.physicalSchemaProducer.ResolveIndices() if err != nil { return err } @@ -717,6 +747,9 @@ func (p *PhysicalTopN) ResolveIndices() (err error) { } p.PartitionBy[i].Col = newCol.(*expression.Column) } + if err := resolveIndexForInlineProjection(&p.physicalSchemaProducer); err != nil { + return err + } return } @@ -733,29 +766,8 @@ func (p *PhysicalLimit) ResolveIndices() (err error) { } p.PartitionBy[i].Col = newCol.(*expression.Column) } - // To avoid that two plan shares the same column slice. - shallowColSlice := make([]*expression.Column, p.schema.Len()) - copy(shallowColSlice, p.schema.Columns) - p.schema = expression.NewSchema(shallowColSlice...) - foundCnt := 0 - // The two column sets are all ordered. And the colsNeedResolving is the subset of the mergedSchema. - // So we can just move forward j if there's no matching is found. - // We don't use the normal ResolvIndices here since there might be duplicate columns in the schema. - // e.g. The schema of child_0 is [col0, col0, col1] - // ResolveIndices will only resolve all col0 reference of the current plan to the first col0. - for i, j := 0, 0; i < p.schema.Len() && j < p.Children()[0].Schema().Len(); { - if !p.schema.Columns[i].EqualColumn(p.Children()[0].Schema().Columns[j]) { - j++ - continue - } - p.schema.Columns[i] = p.schema.Columns[i].Clone().(*expression.Column) - p.schema.Columns[i].Index = j - i++ - j++ - foundCnt++ - } - if foundCnt < p.schema.Len() { - return errors.Errorf("Some columns of %v cannot find the reference from its child(ren)", p.ExplainID().String()) + if err := resolveIndexForInlineProjection(&p.physicalSchemaProducer); err != nil { + return err } return } diff --git a/pkg/planner/core/task.go b/pkg/planner/core/task.go index 82daee30a607c..f7fb6ec00d232 100644 --- a/pkg/planner/core/task.go +++ b/pkg/planner/core/task.go @@ -918,18 +918,16 @@ func (p *PhysicalTopN) getPushedDownTopN(childPlan base.PhysicalPlan, storeTp kv // // Original: DataSource(id, vec) -> TopN(by vec->dis) -> Projection(id) // └─Byitem: vec_distance(vec, '[1,2,3]') - // └─Schema: id, vec // // New: DataSource(id, vec) -> Projection(id, vec->dis) -> TopN(by dis) -> Projection(id) // └─Byitem: dis - // └─Schema: id, dis // // Note that for plan now, TopN has its own schema and does not use the schema of children. if newGlobalTopN != nil { // create a new PhysicalProjection to calculate the distance columns, and add it into plan route bottomProjSchemaCols := make([]*expression.Column, 0, len(childPlan.Schema().Columns)) bottomProjExprs := make([]expression.Expression, 0, len(childPlan.Schema().Columns)) - for _, col := range childPlan.Schema().Columns { + for _, col := range newGlobalTopN.Schema().Columns { newCol := col.Clone().(*expression.Column) bottomProjSchemaCols = append(bottomProjSchemaCols, newCol) bottomProjExprs = append(bottomProjExprs, newCol) @@ -966,8 +964,8 @@ func (p *PhysicalTopN) getPushedDownTopN(childPlan base.PhysicalPlan, storeTp kv Count: newCount, }.Init(p.SCtx(), stats, p.QueryBlockOffset(), p.GetChildReqProps(0)) // mppTask's topN - for _, expr := range distanceCols { - topN.ByItems[expr.Index].Expr = expr.DistanceCol + for _, item := range distanceCols { + topN.ByItems[item.Index].Expr = item.DistanceCol } // rootTask's topn, need reuse the distance col diff --git a/pkg/util/chunk/chunk.go b/pkg/util/chunk/chunk.go index 57a5675c1b2e6..b6c82148b3ba9 100644 --- a/pkg/util/chunk/chunk.go +++ b/pkg/util/chunk/chunk.go @@ -421,6 +421,30 @@ func (c *Chunk) AppendPartialRow(colOff int, row Row) { } } +// AppendRowsByColIdxs appends multiple rows by its colIdxs to the chunk. +// 1. every columns are used if colIdxs is nil. +// 2. no columns are used if colIdxs is not nil but the size of colIdxs is 0. +func (c *Chunk) AppendRowsByColIdxs(rows []Row, colIdxs []int) (wide int) { + if colIdxs == nil { + if len(rows) == 0 { + wide = 0 + return + } + c.AppendRows(rows) + wide = rows[0].Len() * len(rows) + return + } + for _, srcRow := range rows { + c.appendSel(0) + for i, colIdx := range colIdxs { + appendCellByCell(c.columns[i], srcRow.c.columns[colIdx], srcRow.idx) + } + } + c.numVirtualRows += len(rows) + wide = len(colIdxs) * len(rows) + return +} + // AppendRowByColIdxs appends a row to the chunk, using the row's columns specified by colIdxs. // 1. every columns are used if colIdxs is nil. // 2. no columns are used if colIdxs is not nil but the size of colIdxs is 0. diff --git a/pkg/util/chunk/chunk_test.go b/pkg/util/chunk/chunk_test.go index faf4cfbd7b201..14194699ee811 100644 --- a/pkg/util/chunk/chunk_test.go +++ b/pkg/util/chunk/chunk_test.go @@ -1039,6 +1039,46 @@ func TestAppendRows(t *testing.T) { } } +func TestAppendRowsByColIdxs(t *testing.T) { + numCols := 6 + numRows := 10 + chk := newChunk(8, 8, 0, 0, 40, 0) + strFmt := "%d.12345" + for i := range numRows { + chk.AppendNull(0) + chk.AppendInt64(1, int64(i)) + str := fmt.Sprintf(strFmt, i) + chk.AppendString(2, str) + chk.AppendBytes(3, []byte(str)) + chk.AppendMyDecimal(4, types.NewDecFromStringForTest(str)) + chk.AppendJSON(5, types.CreateBinaryJSON(str)) + } + require.Equal(t, numCols, chk.NumCols()) + require.Equal(t, numRows, chk.NumRows()) + + colsIdx := []int{5, 3, 4} + chk2 := newChunk(0, 0, 40) + require.Equal(t, numCols, chk.NumCols()) + rows := make([]Row, numRows) + for i := range numRows { + rows[i] = chk.GetRow(i) + } + wide := chk2.AppendRowsByColIdxs(rows, colsIdx) + require.Equal(t, 10, chk2.NumRows()) + require.Equal(t, numRows*len(colsIdx), wide) + + for i := range numRows { + row := chk2.GetRow(i) + str := fmt.Sprintf(strFmt, i) + require.False(t, row.IsNull(0)) + require.Equal(t, str, string(row.GetJSON(0).GetString())) + require.False(t, row.IsNull(1)) + require.Equal(t, []byte(str), row.GetBytes(1)) + require.False(t, row.IsNull(2)) + require.Equal(t, str, row.GetMyDecimal(2).String()) + } +} + func BenchmarkBatchAppendRows(b *testing.B) { b.ReportAllocs() numRows := 4096 diff --git a/tests/integrationtest/r/executor/index_merge_reader.result b/tests/integrationtest/r/executor/index_merge_reader.result index 7a9ad2f08db27..5fdb7f1203bcd 100644 --- a/tests/integrationtest/r/executor/index_merge_reader.result +++ b/tests/integrationtest/r/executor/index_merge_reader.result @@ -486,13 +486,12 @@ begin; insert into t values(1, 1, -3); explain select /*+ USE_INDEX_MERGE(t, idx1, idx2) */ * from t where a = 1 or b = 1 order by c limit 2; id estRows task access object operator info -Projection_8 2.00 root executor__index_merge_reader.t.a, executor__index_merge_reader.t.b, executor__index_merge_reader.t.c -└─Limit_15 2.00 root offset:0, count:2 - └─UnionScan_21 2.00 root or(eq(executor__index_merge_reader.t.a, 1), eq(executor__index_merge_reader.t.b, 1)) - └─IndexMerge_25 2.00 root type: union - ├─IndexRangeScan_22(Build) 1.00 cop[tikv] table:t, index:idx1(a, c) range:[1,1], keep order:true, stats:pseudo - ├─IndexRangeScan_23(Build) 1.00 cop[tikv] table:t, index:idx2(b, c) range:[1,1], keep order:true, stats:pseudo - └─TableRowIDScan_24(Probe) 2.00 cop[tikv] table:t keep order:false, stats:pseudo +Limit_15 2.00 root offset:0, count:2 +└─UnionScan_21 2.00 root or(eq(executor__index_merge_reader.t.a, 1), eq(executor__index_merge_reader.t.b, 1)) + └─IndexMerge_25 2.00 root type: union + ├─IndexRangeScan_22(Build) 1.00 cop[tikv] table:t, index:idx1(a, c) range:[1,1], keep order:true, stats:pseudo + ├─IndexRangeScan_23(Build) 1.00 cop[tikv] table:t, index:idx2(b, c) range:[1,1], keep order:true, stats:pseudo + └─TableRowIDScan_24(Probe) 2.00 cop[tikv] table:t keep order:false, stats:pseudo select /*+ USE_INDEX_MERGE(t, idx1, idx2) */ * from t where a = 1 or b = 1 order by c limit 2; a b c 1 1 -3 @@ -502,13 +501,12 @@ begin; insert into t values(1, 2, 4); explain select /*+ USE_INDEX_MERGE(t, idx1, idx2) */ * from t where a = 1 or b = 1 order by c desc limit 2; id estRows task access object operator info -Projection_8 2.00 root executor__index_merge_reader.t.a, executor__index_merge_reader.t.b, executor__index_merge_reader.t.c -└─Limit_15 2.00 root offset:0, count:2 - └─UnionScan_21 2.00 root or(eq(executor__index_merge_reader.t.a, 1), eq(executor__index_merge_reader.t.b, 1)) - └─IndexMerge_25 2.00 root type: union - ├─IndexRangeScan_22(Build) 1.00 cop[tikv] table:t, index:idx1(a, c) range:[1,1], keep order:true, desc, stats:pseudo - ├─IndexRangeScan_23(Build) 1.00 cop[tikv] table:t, index:idx2(b, c) range:[1,1], keep order:true, desc, stats:pseudo - └─TableRowIDScan_24(Probe) 2.00 cop[tikv] table:t keep order:false, stats:pseudo +Limit_15 2.00 root offset:0, count:2 +└─UnionScan_21 2.00 root or(eq(executor__index_merge_reader.t.a, 1), eq(executor__index_merge_reader.t.b, 1)) + └─IndexMerge_25 2.00 root type: union + ├─IndexRangeScan_22(Build) 1.00 cop[tikv] table:t, index:idx1(a, c) range:[1,1], keep order:true, desc, stats:pseudo + ├─IndexRangeScan_23(Build) 1.00 cop[tikv] table:t, index:idx2(b, c) range:[1,1], keep order:true, desc, stats:pseudo + └─TableRowIDScan_24(Probe) 2.00 cop[tikv] table:t keep order:false, stats:pseudo select /*+ USE_INDEX_MERGE(t, idx1, idx2) */ * from t where a = 1 or b = 1 order by c desc limit 2; a b c 1 2 4 diff --git a/tests/integrationtest/r/explain_easy.result b/tests/integrationtest/r/explain_easy.result index b86a41170300b..1c84e1545a375 100644 --- a/tests/integrationtest/r/explain_easy.result +++ b/tests/integrationtest/r/explain_easy.result @@ -130,11 +130,12 @@ Projection 10000.00 root eq(explain_easy.t1.c2, explain_easy.t2.c2)->Column#11 └─Apply 10000.00 root CARTESIAN left outer join, left side:TableReader ├─TableReader(Build) 10000.00 root data:TableFullScan │ └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo - └─Projection(Probe) 10000.00 root explain_easy.t2.c1, explain_easy.t2.c2 - └─IndexLookUp 10000.00 root limit embedded(offset:0, count:1) - ├─Limit(Build) 10000.00 cop[tikv] offset:0, count:1 - │ └─IndexRangeScan 10000.00 cop[tikv] table:t2, index:c1(c1) range: decided by [eq(explain_easy.t1.c1, explain_easy.t2.c1)], keep order:true, stats:pseudo - └─TableRowIDScan(Probe) 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo + └─Projection(Probe) 10000.00 root explain_easy.t2.c2 + └─Projection 10000.00 root explain_easy.t2.c1, explain_easy.t2.c2 + └─IndexLookUp 10000.00 root limit embedded(offset:0, count:1) + ├─Limit(Build) 10000.00 cop[tikv] offset:0, count:1 + │ └─IndexRangeScan 10000.00 cop[tikv] table:t2, index:c1(c1) range: decided by [eq(explain_easy.t1.c1, explain_easy.t2.c1)], keep order:true, stats:pseudo + └─TableRowIDScan(Probe) 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo explain format = 'brief' select * from t1 order by c1 desc limit 1; id estRows task access object operator info Limit 1.00 root offset:0, count:1 diff --git a/tests/integrationtest/r/globalindex/mem_index_merge.result b/tests/integrationtest/r/globalindex/mem_index_merge.result index 0f2db4c3424c6..f61d589ada36a 100644 --- a/tests/integrationtest/r/globalindex/mem_index_merge.result +++ b/tests/integrationtest/r/globalindex/mem_index_merge.result @@ -157,25 +157,23 @@ a b c d ## for indexMerge union in txn with order by limit explain select /*+ use_index_merge(tpk2, uidx_ac, idx_bc) */ * from tpk2 where a = 1 or b = 4 order by c limit 1; id estRows task access object operator info -Projection_8 1.00 root globalindex__mem_index_merge.tpk2.a, globalindex__mem_index_merge.tpk2.b, globalindex__mem_index_merge.tpk2.c, globalindex__mem_index_merge.tpk2.d -└─Limit_15 1.00 root offset:0, count:1 - └─UnionScan_22 1.00 root or(eq(globalindex__mem_index_merge.tpk2.a, 1), eq(globalindex__mem_index_merge.tpk2.b, 4)) - └─IndexMerge_27 1.00 root partition:all type: union - ├─IndexRangeScan_23(Build) 0.91 cop[tikv] table:tpk2, index:uidx_ac(a, c) range:[1,1], keep order:true, stats:pseudo - ├─IndexRangeScan_25(Build) 0.91 cop[tikv] table:tpk2, index:idx_bc(b, c) range:[4,4], keep order:true, stats:pseudo - └─TableRowIDScan_26(Probe) 1.00 cop[tikv] table:tpk2 keep order:false, stats:pseudo +Limit_15 1.00 root offset:0, count:1 +└─UnionScan_22 1.00 root or(eq(globalindex__mem_index_merge.tpk2.a, 1), eq(globalindex__mem_index_merge.tpk2.b, 4)) + └─IndexMerge_27 1.00 root partition:all type: union + ├─IndexRangeScan_23(Build) 0.91 cop[tikv] table:tpk2, index:uidx_ac(a, c) range:[1,1], keep order:true, stats:pseudo + ├─IndexRangeScan_25(Build) 0.91 cop[tikv] table:tpk2, index:idx_bc(b, c) range:[4,4], keep order:true, stats:pseudo + └─TableRowIDScan_26(Probe) 1.00 cop[tikv] table:tpk2 keep order:false, stats:pseudo select /*+ use_index_merge(tpk2, uidx_ac, idx_bc) */ * from tpk2 where a = 1 or b = 4 order by c limit 1; a b c d 1 2 1 1 explain select /*+ use_index_merge(tpk2, uidx_ac, idx_bc) */ * from tpk2 where a = 1 or b = 4 order by c desc limit 1; id estRows task access object operator info -Projection_8 1.00 root globalindex__mem_index_merge.tpk2.a, globalindex__mem_index_merge.tpk2.b, globalindex__mem_index_merge.tpk2.c, globalindex__mem_index_merge.tpk2.d -└─Limit_15 1.00 root offset:0, count:1 - └─UnionScan_22 1.00 root or(eq(globalindex__mem_index_merge.tpk2.a, 1), eq(globalindex__mem_index_merge.tpk2.b, 4)) - └─IndexMerge_27 1.00 root partition:all type: union - ├─IndexRangeScan_23(Build) 0.91 cop[tikv] table:tpk2, index:uidx_ac(a, c) range:[1,1], keep order:true, desc, stats:pseudo - ├─IndexRangeScan_25(Build) 0.91 cop[tikv] table:tpk2, index:idx_bc(b, c) range:[4,4], keep order:true, desc, stats:pseudo - └─TableRowIDScan_26(Probe) 1.00 cop[tikv] table:tpk2 keep order:false, stats:pseudo +Limit_15 1.00 root offset:0, count:1 +└─UnionScan_22 1.00 root or(eq(globalindex__mem_index_merge.tpk2.a, 1), eq(globalindex__mem_index_merge.tpk2.b, 4)) + └─IndexMerge_27 1.00 root partition:all type: union + ├─IndexRangeScan_23(Build) 0.91 cop[tikv] table:tpk2, index:uidx_ac(a, c) range:[1,1], keep order:true, desc, stats:pseudo + ├─IndexRangeScan_25(Build) 0.91 cop[tikv] table:tpk2, index:idx_bc(b, c) range:[4,4], keep order:true, desc, stats:pseudo + └─TableRowIDScan_26(Probe) 1.00 cop[tikv] table:tpk2 keep order:false, stats:pseudo select /*+ use_index_merge(tpk2, uidx_ac, idx_bc) */ * from tpk2 where a = 1 or b = 4 order by c desc limit 1; a b c d 2 4 2 2 diff --git a/tests/integrationtest/r/planner/core/casetest/integration.result b/tests/integrationtest/r/planner/core/casetest/integration.result index 12f95552544ad..4a623ec1553c3 100644 --- a/tests/integrationtest/r/planner/core/casetest/integration.result +++ b/tests/integrationtest/r/planner/core/casetest/integration.result @@ -184,14 +184,13 @@ Projection 9990.00 root planner__core__casetest__integration.t.b desc format = 'brief' select t1.a from t t1 order by (t1.b = 1 and exists (select 1 from t t2 where t1.b = t2.b)) limit 1; id estRows task access object operator info Projection 1.00 root planner__core__casetest__integration.t.a -└─Projection 1.00 root planner__core__casetest__integration.t.a, planner__core__casetest__integration.t.b, Column#11 - └─TopN 1.00 root Column#13, offset:0, count:1 - └─Projection 10000.00 root planner__core__casetest__integration.t.a, planner__core__casetest__integration.t.b, Column#11, and(eq(planner__core__casetest__integration.t.b, 1), Column#11)->Column#13 - └─HashJoin 10000.00 root left outer semi join, left side:TableReader, equal:[eq(planner__core__casetest__integration.t.b, planner__core__casetest__integration.t.b)] - ├─TableReader(Build) 10000.00 root data:TableFullScan - │ └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo - └─TableReader(Probe) 10000.00 root data:TableFullScan - └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo +└─TopN 1.00 root Column#13, offset:0, count:1 + └─Projection 10000.00 root planner__core__casetest__integration.t.a, planner__core__casetest__integration.t.b, Column#11, and(eq(planner__core__casetest__integration.t.b, 1), Column#11)->Column#13 + └─HashJoin 10000.00 root left outer semi join, left side:TableReader, equal:[eq(planner__core__casetest__integration.t.b, planner__core__casetest__integration.t.b)] + ├─TableReader(Build) 10000.00 root data:TableFullScan + │ └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo + └─TableReader(Probe) 10000.00 root data:TableFullScan + └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo desc format = 'brief' select * from (select b+b as x from t) t1, t t2 where t1.x=t2.b order by t1.x limit 1; id estRows task access object operator info TopN 1.00 root Column#4, offset:0, count:1 @@ -276,12 +275,11 @@ c 2 explain format = 'brief' select /*+ stream_agg() */ count(*) c from t group by a order by a limit 1; id estRows task access object operator info -Projection 1.00 root Column#3->Column#4 -└─Limit 1.00 root offset:0, count:1 - └─StreamAgg 1.00 root group by:planner__core__casetest__integration.t.a, funcs:count(1)->Column#3, funcs:firstrow(planner__core__casetest__integration.t.a)->planner__core__casetest__integration.t.a - └─Sort 1.25 root planner__core__casetest__integration.t.a - └─TableReader 1.25 root data:TableFullScan - └─TableFullScan 1.25 cop[tikv] table:t keep order:false, stats:pseudo +Limit 1.00 root offset:0, count:1 +└─StreamAgg 1.00 root group by:planner__core__casetest__integration.t.a, funcs:count(1)->Column#3, funcs:firstrow(planner__core__casetest__integration.t.a)->planner__core__casetest__integration.t.a + └─Sort 1.25 root planner__core__casetest__integration.t.a + └─TableReader 1.25 root data:TableFullScan + └─TableFullScan 1.25 cop[tikv] table:t keep order:false, stats:pseudo select /*+ stream_agg() */ count(*) c from t group by a order by a limit 1; c 2 diff --git a/tests/integrationtest/r/planner/core/casetest/partition/integration_partition.result b/tests/integrationtest/r/planner/core/casetest/partition/integration_partition.result index 6e62a8bd6392d..753879d038aeb 100644 --- a/tests/integrationtest/r/planner/core/casetest/partition/integration_partition.result +++ b/tests/integrationtest/r/planner/core/casetest/partition/integration_partition.result @@ -526,204 +526,187 @@ TopN 10.00 root list_partition_pruning.thash.a, offset:0, count:10 └─TableRowIDScan(Probe) 10.00 cop[tikv] table:thash, partition:p3 keep order:false, stats:pseudo explain format='brief' select a from t use index (ia) where a > 10 and c = 10 order by a limit 10; id estRows task access object operator info -Projection 3.33 root list_partition_pruning.t.a -└─Limit 3.33 root offset:0, count:10 - └─Projection 3.33 root list_partition_pruning.t.a, list_partition_pruning.t.c - └─IndexLookUp 3.33 root - ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:t, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo - └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.t.c, 10) - └─TableRowIDScan 3333.33 cop[tikv] table:t keep order:false, stats:pseudo +Limit 3.33 root offset:0, count:10 +└─Projection 3.33 root list_partition_pruning.t.a, list_partition_pruning.t.c + └─IndexLookUp 3.33 root + ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:t, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo + └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.t.c, 10) + └─TableRowIDScan 3333.33 cop[tikv] table:t keep order:false, stats:pseudo explain format='brief' select a from trange use index (ia) where a > 10 and c = 10 order by a limit 10; id estRows task access object operator info TopN 10.00 root list_partition_pruning.trange.a, offset:0, count:10 └─PartitionUnion 10.00 root - ├─Projection 3.33 root list_partition_pruning.trange.a - │ └─Limit 3.33 root offset:0, count:10 - │ └─Projection 3.33 root list_partition_pruning.trange.a, list_partition_pruning.trange.c - │ └─IndexLookUp 3.33 root - │ ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:trange, partition:p1, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo - │ └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.trange.c, 10) - │ └─TableRowIDScan 3333.33 cop[tikv] table:trange, partition:p1 keep order:false, stats:pseudo - ├─Projection 3.33 root list_partition_pruning.trange.a - │ └─Limit 3.33 root offset:0, count:10 - │ └─Projection 3.33 root list_partition_pruning.trange.a, list_partition_pruning.trange.c - │ └─IndexLookUp 3.33 root - │ ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:trange, partition:p2, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo - │ └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.trange.c, 10) - │ └─TableRowIDScan 3333.33 cop[tikv] table:trange, partition:p2 keep order:false, stats:pseudo - └─Projection 3.33 root list_partition_pruning.trange.a - └─Limit 3.33 root offset:0, count:10 - └─Projection 3.33 root list_partition_pruning.trange.a, list_partition_pruning.trange.c - └─IndexLookUp 3.33 root - ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:trange, partition:p3, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo - └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.trange.c, 10) - └─TableRowIDScan 3333.33 cop[tikv] table:trange, partition:p3 keep order:false, stats:pseudo + ├─Limit 3.33 root offset:0, count:10 + │ └─Projection 3.33 root list_partition_pruning.trange.a, list_partition_pruning.trange.c + │ └─IndexLookUp 3.33 root + │ ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:trange, partition:p1, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo + │ └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.trange.c, 10) + │ └─TableRowIDScan 3333.33 cop[tikv] table:trange, partition:p1 keep order:false, stats:pseudo + ├─Limit 3.33 root offset:0, count:10 + │ └─Projection 3.33 root list_partition_pruning.trange.a, list_partition_pruning.trange.c + │ └─IndexLookUp 3.33 root + │ ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:trange, partition:p2, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo + │ └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.trange.c, 10) + │ └─TableRowIDScan 3333.33 cop[tikv] table:trange, partition:p2 keep order:false, stats:pseudo + └─Limit 3.33 root offset:0, count:10 + └─Projection 3.33 root list_partition_pruning.trange.a, list_partition_pruning.trange.c + └─IndexLookUp 3.33 root + ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:trange, partition:p3, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo + └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.trange.c, 10) + └─TableRowIDScan 3333.33 cop[tikv] table:trange, partition:p3 keep order:false, stats:pseudo explain format='brief' select a from tlist use index (ia) where a > 10 and c = 10 order by a limit 10; id estRows task access object operator info TopN 6.67 root list_partition_pruning.tlist.a, offset:0, count:10 └─PartitionUnion 6.67 root - ├─Projection 3.33 root list_partition_pruning.tlist.a - │ └─Limit 3.33 root offset:0, count:10 - │ └─Projection 3.33 root list_partition_pruning.tlist.a, list_partition_pruning.tlist.c - │ └─IndexLookUp 3.33 root - │ ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:tlist, partition:p0, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo - │ └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.tlist.c, 10) - │ └─TableRowIDScan 3333.33 cop[tikv] table:tlist, partition:p0 keep order:false, stats:pseudo - └─Projection 3.33 root list_partition_pruning.tlist.a - └─Limit 3.33 root offset:0, count:10 - └─Projection 3.33 root list_partition_pruning.tlist.a, list_partition_pruning.tlist.c - └─IndexLookUp 3.33 root - ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:tlist, partition:p1, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo - └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.tlist.c, 10) - └─TableRowIDScan 3333.33 cop[tikv] table:tlist, partition:p1 keep order:false, stats:pseudo + ├─Limit 3.33 root offset:0, count:10 + │ └─Projection 3.33 root list_partition_pruning.tlist.a, list_partition_pruning.tlist.c + │ └─IndexLookUp 3.33 root + │ ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:tlist, partition:p0, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo + │ └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.tlist.c, 10) + │ └─TableRowIDScan 3333.33 cop[tikv] table:tlist, partition:p0 keep order:false, stats:pseudo + └─Limit 3.33 root offset:0, count:10 + └─Projection 3.33 root list_partition_pruning.tlist.a, list_partition_pruning.tlist.c + └─IndexLookUp 3.33 root + ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:tlist, partition:p1, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo + └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.tlist.c, 10) + └─TableRowIDScan 3333.33 cop[tikv] table:tlist, partition:p1 keep order:false, stats:pseudo explain format='brief' select a from thash use index (ia) where a > 10 and c = 10 order by a limit 10; id estRows task access object operator info TopN 10.00 root list_partition_pruning.thash.a, offset:0, count:10 └─PartitionUnion 13.33 root - ├─Projection 3.33 root list_partition_pruning.thash.a - │ └─Limit 3.33 root offset:0, count:10 - │ └─Projection 3.33 root list_partition_pruning.thash.a, list_partition_pruning.thash.c - │ └─IndexLookUp 3.33 root - │ ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:thash, partition:p0, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo - │ └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.thash.c, 10) - │ └─TableRowIDScan 3333.33 cop[tikv] table:thash, partition:p0 keep order:false, stats:pseudo - ├─Projection 3.33 root list_partition_pruning.thash.a - │ └─Limit 3.33 root offset:0, count:10 - │ └─Projection 3.33 root list_partition_pruning.thash.a, list_partition_pruning.thash.c - │ └─IndexLookUp 3.33 root - │ ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:thash, partition:p1, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo - │ └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.thash.c, 10) - │ └─TableRowIDScan 3333.33 cop[tikv] table:thash, partition:p1 keep order:false, stats:pseudo - ├─Projection 3.33 root list_partition_pruning.thash.a - │ └─Limit 3.33 root offset:0, count:10 - │ └─Projection 3.33 root list_partition_pruning.thash.a, list_partition_pruning.thash.c - │ └─IndexLookUp 3.33 root - │ ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:thash, partition:p2, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo - │ └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.thash.c, 10) - │ └─TableRowIDScan 3333.33 cop[tikv] table:thash, partition:p2 keep order:false, stats:pseudo - └─Projection 3.33 root list_partition_pruning.thash.a - └─Limit 3.33 root offset:0, count:10 - └─Projection 3.33 root list_partition_pruning.thash.a, list_partition_pruning.thash.c - └─IndexLookUp 3.33 root - ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:thash, partition:p3, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo - └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.thash.c, 10) - └─TableRowIDScan 3333.33 cop[tikv] table:thash, partition:p3 keep order:false, stats:pseudo + ├─Limit 3.33 root offset:0, count:10 + │ └─Projection 3.33 root list_partition_pruning.thash.a, list_partition_pruning.thash.c + │ └─IndexLookUp 3.33 root + │ ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:thash, partition:p0, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo + │ └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.thash.c, 10) + │ └─TableRowIDScan 3333.33 cop[tikv] table:thash, partition:p0 keep order:false, stats:pseudo + ├─Limit 3.33 root offset:0, count:10 + │ └─Projection 3.33 root list_partition_pruning.thash.a, list_partition_pruning.thash.c + │ └─IndexLookUp 3.33 root + │ ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:thash, partition:p1, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo + │ └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.thash.c, 10) + │ └─TableRowIDScan 3333.33 cop[tikv] table:thash, partition:p1 keep order:false, stats:pseudo + ├─Limit 3.33 root offset:0, count:10 + │ └─Projection 3.33 root list_partition_pruning.thash.a, list_partition_pruning.thash.c + │ └─IndexLookUp 3.33 root + │ ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:thash, partition:p2, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo + │ └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.thash.c, 10) + │ └─TableRowIDScan 3333.33 cop[tikv] table:thash, partition:p2 keep order:false, stats:pseudo + └─Limit 3.33 root offset:0, count:10 + └─Projection 3.33 root list_partition_pruning.thash.a, list_partition_pruning.thash.c + └─IndexLookUp 3.33 root + ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:thash, partition:p3, index:ia(a) range:(10,+inf], keep order:true, stats:pseudo + └─Selection(Probe) 3.33 cop[tikv] eq(list_partition_pruning.thash.c, 10) + └─TableRowIDScan 3333.33 cop[tikv] table:thash, partition:p3 keep order:false, stats:pseudo explain format='brief' select a from t use index () where b > 10 order by b limit 10; id estRows task access object operator info -Projection 10.00 root list_partition_pruning.t.a -└─Limit 10.00 root offset:0, count:10 - └─TableReader 10.00 root data:Limit - └─Limit 10.00 cop[tikv] offset:0, count:10 - └─TableRangeScan 10.00 cop[tikv] table:t range:(10,+inf], keep order:true, stats:pseudo +Limit 10.00 root offset:0, count:10 +└─TableReader 10.00 root data:Limit + └─Limit 10.00 cop[tikv] offset:0, count:10 + └─TableRangeScan 10.00 cop[tikv] table:t range:(10,+inf], keep order:true, stats:pseudo explain format='brief' select a from trange use index () where b > 10 order by b limit 10; id estRows task access object operator info -Projection 10.00 root list_partition_pruning.trange.a -└─TopN 10.00 root list_partition_pruning.trange.b, offset:0, count:10 - └─PartitionUnion 30.00 root - ├─Limit 10.00 root offset:0, count:10 - │ └─TableReader 10.00 root data:Limit - │ └─Limit 10.00 cop[tikv] offset:0, count:10 - │ └─TableRangeScan 10.00 cop[tikv] table:trange, partition:p1 range:(10,+inf], keep order:true, stats:pseudo - ├─Limit 10.00 root offset:0, count:10 - │ └─TableReader 10.00 root data:Limit - │ └─Limit 10.00 cop[tikv] offset:0, count:10 - │ └─TableRangeScan 10.00 cop[tikv] table:trange, partition:p2 range:(10,+inf], keep order:true, stats:pseudo - └─Limit 10.00 root offset:0, count:10 - └─TableReader 10.00 root data:Limit - └─Limit 10.00 cop[tikv] offset:0, count:10 - └─TableRangeScan 10.00 cop[tikv] table:trange, partition:p3 range:(10,+inf], keep order:true, stats:pseudo +TopN 10.00 root list_partition_pruning.trange.b, offset:0, count:10 +└─PartitionUnion 30.00 root + ├─Limit 10.00 root offset:0, count:10 + │ └─TableReader 10.00 root data:Limit + │ └─Limit 10.00 cop[tikv] offset:0, count:10 + │ └─TableRangeScan 10.00 cop[tikv] table:trange, partition:p1 range:(10,+inf], keep order:true, stats:pseudo + ├─Limit 10.00 root offset:0, count:10 + │ └─TableReader 10.00 root data:Limit + │ └─Limit 10.00 cop[tikv] offset:0, count:10 + │ └─TableRangeScan 10.00 cop[tikv] table:trange, partition:p2 range:(10,+inf], keep order:true, stats:pseudo + └─Limit 10.00 root offset:0, count:10 + └─TableReader 10.00 root data:Limit + └─Limit 10.00 cop[tikv] offset:0, count:10 + └─TableRangeScan 10.00 cop[tikv] table:trange, partition:p3 range:(10,+inf], keep order:true, stats:pseudo explain format='brief' select a from tlist use index () where b > 10 order by b limit 10; id estRows task access object operator info TableDual 0.00 root rows:0 explain format='brief' select a from thash use index () where b > 10 order by b limit 10; id estRows task access object operator info -Projection 10.00 root list_partition_pruning.thash.a -└─TopN 10.00 root list_partition_pruning.thash.b, offset:0, count:10 - └─PartitionUnion 40.00 root - ├─Limit 10.00 root offset:0, count:10 - │ └─TableReader 10.00 root data:Limit - │ └─Limit 10.00 cop[tikv] offset:0, count:10 - │ └─TableRangeScan 10.00 cop[tikv] table:thash, partition:p0 range:(10,+inf], keep order:true, stats:pseudo - ├─Limit 10.00 root offset:0, count:10 - │ └─TableReader 10.00 root data:Limit - │ └─Limit 10.00 cop[tikv] offset:0, count:10 - │ └─TableRangeScan 10.00 cop[tikv] table:thash, partition:p1 range:(10,+inf], keep order:true, stats:pseudo - ├─Limit 10.00 root offset:0, count:10 - │ └─TableReader 10.00 root data:Limit - │ └─Limit 10.00 cop[tikv] offset:0, count:10 - │ └─TableRangeScan 10.00 cop[tikv] table:thash, partition:p2 range:(10,+inf], keep order:true, stats:pseudo - └─Limit 10.00 root offset:0, count:10 - └─TableReader 10.00 root data:Limit - └─Limit 10.00 cop[tikv] offset:0, count:10 - └─TableRangeScan 10.00 cop[tikv] table:thash, partition:p3 range:(10,+inf], keep order:true, stats:pseudo +TopN 10.00 root list_partition_pruning.thash.b, offset:0, count:10 +└─PartitionUnion 40.00 root + ├─Limit 10.00 root offset:0, count:10 + │ └─TableReader 10.00 root data:Limit + │ └─Limit 10.00 cop[tikv] offset:0, count:10 + │ └─TableRangeScan 10.00 cop[tikv] table:thash, partition:p0 range:(10,+inf], keep order:true, stats:pseudo + ├─Limit 10.00 root offset:0, count:10 + │ └─TableReader 10.00 root data:Limit + │ └─Limit 10.00 cop[tikv] offset:0, count:10 + │ └─TableRangeScan 10.00 cop[tikv] table:thash, partition:p1 range:(10,+inf], keep order:true, stats:pseudo + ├─Limit 10.00 root offset:0, count:10 + │ └─TableReader 10.00 root data:Limit + │ └─Limit 10.00 cop[tikv] offset:0, count:10 + │ └─TableRangeScan 10.00 cop[tikv] table:thash, partition:p2 range:(10,+inf], keep order:true, stats:pseudo + └─Limit 10.00 root offset:0, count:10 + └─TableReader 10.00 root data:Limit + └─Limit 10.00 cop[tikv] offset:0, count:10 + └─TableRangeScan 10.00 cop[tikv] table:thash, partition:p3 range:(10,+inf], keep order:true, stats:pseudo explain format='brief' select a from t use index () where a > 10 order by b limit 10; id estRows task access object operator info -Projection 10.00 root list_partition_pruning.t.a -└─Limit 10.00 root offset:0, count:10 - └─TableReader 10.00 root data:Limit - └─Limit 10.00 cop[tikv] offset:0, count:10 - └─Selection 10.00 cop[tikv] gt(list_partition_pruning.t.a, 10) - └─TableFullScan 30.00 cop[tikv] table:t keep order:true, stats:pseudo +Limit 10.00 root offset:0, count:10 +└─TableReader 10.00 root data:Limit + └─Limit 10.00 cop[tikv] offset:0, count:10 + └─Selection 10.00 cop[tikv] gt(list_partition_pruning.t.a, 10) + └─TableFullScan 30.00 cop[tikv] table:t keep order:true, stats:pseudo explain format='brief' select a from trange use index () where a > 10 order by b limit 10; id estRows task access object operator info -Projection 10.00 root list_partition_pruning.trange.a -└─TopN 10.00 root list_partition_pruning.trange.b, offset:0, count:10 - └─PartitionUnion 30.00 root - ├─Limit 10.00 root offset:0, count:10 - │ └─TableReader 10.00 root data:Limit - │ └─Limit 10.00 cop[tikv] offset:0, count:10 - │ └─Selection 10.00 cop[tikv] gt(list_partition_pruning.trange.a, 10) - │ └─TableFullScan 30.00 cop[tikv] table:trange, partition:p1 keep order:true, stats:pseudo - ├─Limit 10.00 root offset:0, count:10 - │ └─TableReader 10.00 root data:Limit - │ └─Limit 10.00 cop[tikv] offset:0, count:10 - │ └─Selection 10.00 cop[tikv] gt(list_partition_pruning.trange.a, 10) - │ └─TableFullScan 30.00 cop[tikv] table:trange, partition:p2 keep order:true, stats:pseudo - └─Limit 10.00 root offset:0, count:10 - └─TableReader 10.00 root data:Limit - └─Limit 10.00 cop[tikv] offset:0, count:10 - └─Selection 10.00 cop[tikv] gt(list_partition_pruning.trange.a, 10) - └─TableFullScan 30.00 cop[tikv] table:trange, partition:p3 keep order:true, stats:pseudo +TopN 10.00 root list_partition_pruning.trange.b, offset:0, count:10 +└─PartitionUnion 30.00 root + ├─Limit 10.00 root offset:0, count:10 + │ └─TableReader 10.00 root data:Limit + │ └─Limit 10.00 cop[tikv] offset:0, count:10 + │ └─Selection 10.00 cop[tikv] gt(list_partition_pruning.trange.a, 10) + │ └─TableFullScan 30.00 cop[tikv] table:trange, partition:p1 keep order:true, stats:pseudo + ├─Limit 10.00 root offset:0, count:10 + │ └─TableReader 10.00 root data:Limit + │ └─Limit 10.00 cop[tikv] offset:0, count:10 + │ └─Selection 10.00 cop[tikv] gt(list_partition_pruning.trange.a, 10) + │ └─TableFullScan 30.00 cop[tikv] table:trange, partition:p2 keep order:true, stats:pseudo + └─Limit 10.00 root offset:0, count:10 + └─TableReader 10.00 root data:Limit + └─Limit 10.00 cop[tikv] offset:0, count:10 + └─Selection 10.00 cop[tikv] gt(list_partition_pruning.trange.a, 10) + └─TableFullScan 30.00 cop[tikv] table:trange, partition:p3 keep order:true, stats:pseudo explain format='brief' select a from tlist use index () where a > 10 order by b limit 10; id estRows task access object operator info -Projection 10.00 root list_partition_pruning.tlist.a -└─TopN 10.00 root list_partition_pruning.tlist.b, offset:0, count:10 - └─PartitionUnion 20.00 root - ├─Limit 10.00 root offset:0, count:10 - │ └─TableReader 10.00 root data:Limit - │ └─Limit 10.00 cop[tikv] offset:0, count:10 - │ └─Selection 10.00 cop[tikv] gt(list_partition_pruning.tlist.a, 10) - │ └─TableFullScan 30.00 cop[tikv] table:tlist, partition:p0 keep order:true, stats:pseudo - └─Limit 10.00 root offset:0, count:10 - └─TableReader 10.00 root data:Limit - └─Limit 10.00 cop[tikv] offset:0, count:10 - └─Selection 10.00 cop[tikv] gt(list_partition_pruning.tlist.a, 10) - └─TableFullScan 30.00 cop[tikv] table:tlist, partition:p1 keep order:true, stats:pseudo +TopN 10.00 root list_partition_pruning.tlist.b, offset:0, count:10 +└─PartitionUnion 20.00 root + ├─Limit 10.00 root offset:0, count:10 + │ └─TableReader 10.00 root data:Limit + │ └─Limit 10.00 cop[tikv] offset:0, count:10 + │ └─Selection 10.00 cop[tikv] gt(list_partition_pruning.tlist.a, 10) + │ └─TableFullScan 30.00 cop[tikv] table:tlist, partition:p0 keep order:true, stats:pseudo + └─Limit 10.00 root offset:0, count:10 + └─TableReader 10.00 root data:Limit + └─Limit 10.00 cop[tikv] offset:0, count:10 + └─Selection 10.00 cop[tikv] gt(list_partition_pruning.tlist.a, 10) + └─TableFullScan 30.00 cop[tikv] table:tlist, partition:p1 keep order:true, stats:pseudo explain format='brief' select a from thash use index () where a > 10 order by b limit 10; id estRows task access object operator info -Projection 10.00 root list_partition_pruning.thash.a -└─TopN 10.00 root list_partition_pruning.thash.b, offset:0, count:10 - └─PartitionUnion 40.00 root - ├─Limit 10.00 root offset:0, count:10 - │ └─TableReader 10.00 root data:Limit - │ └─Limit 10.00 cop[tikv] offset:0, count:10 - │ └─Selection 10.00 cop[tikv] gt(list_partition_pruning.thash.a, 10) - │ └─TableFullScan 30.00 cop[tikv] table:thash, partition:p0 keep order:true, stats:pseudo - ├─Limit 10.00 root offset:0, count:10 - │ └─TableReader 10.00 root data:Limit - │ └─Limit 10.00 cop[tikv] offset:0, count:10 - │ └─Selection 10.00 cop[tikv] gt(list_partition_pruning.thash.a, 10) - │ └─TableFullScan 30.00 cop[tikv] table:thash, partition:p1 keep order:true, stats:pseudo - ├─Limit 10.00 root offset:0, count:10 - │ └─TableReader 10.00 root data:Limit - │ └─Limit 10.00 cop[tikv] offset:0, count:10 - │ └─Selection 10.00 cop[tikv] gt(list_partition_pruning.thash.a, 10) - │ └─TableFullScan 30.00 cop[tikv] table:thash, partition:p2 keep order:true, stats:pseudo - └─Limit 10.00 root offset:0, count:10 - └─TableReader 10.00 root data:Limit - └─Limit 10.00 cop[tikv] offset:0, count:10 - └─Selection 10.00 cop[tikv] gt(list_partition_pruning.thash.a, 10) - └─TableFullScan 30.00 cop[tikv] table:thash, partition:p3 keep order:true, stats:pseudo +TopN 10.00 root list_partition_pruning.thash.b, offset:0, count:10 +└─PartitionUnion 40.00 root + ├─Limit 10.00 root offset:0, count:10 + │ └─TableReader 10.00 root data:Limit + │ └─Limit 10.00 cop[tikv] offset:0, count:10 + │ └─Selection 10.00 cop[tikv] gt(list_partition_pruning.thash.a, 10) + │ └─TableFullScan 30.00 cop[tikv] table:thash, partition:p0 keep order:true, stats:pseudo + ├─Limit 10.00 root offset:0, count:10 + │ └─TableReader 10.00 root data:Limit + │ └─Limit 10.00 cop[tikv] offset:0, count:10 + │ └─Selection 10.00 cop[tikv] gt(list_partition_pruning.thash.a, 10) + │ └─TableFullScan 30.00 cop[tikv] table:thash, partition:p1 keep order:true, stats:pseudo + ├─Limit 10.00 root offset:0, count:10 + │ └─TableReader 10.00 root data:Limit + │ └─Limit 10.00 cop[tikv] offset:0, count:10 + │ └─Selection 10.00 cop[tikv] gt(list_partition_pruning.thash.a, 10) + │ └─TableFullScan 30.00 cop[tikv] table:thash, partition:p2 keep order:true, stats:pseudo + └─Limit 10.00 root offset:0, count:10 + └─TableReader 10.00 root data:Limit + └─Limit 10.00 cop[tikv] offset:0, count:10 + └─Selection 10.00 cop[tikv] gt(list_partition_pruning.thash.a, 10) + └─TableFullScan 30.00 cop[tikv] table:thash, partition:p3 keep order:true, stats:pseudo drop table if exists t; create table t(col varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL) PARTITION BY KEY (`col`) PARTITIONS 7; explain format = brief select * from t where col = 'linpin'; diff --git a/tests/integrationtest/r/planner/core/casetest/rule/rule_result_reorder.result b/tests/integrationtest/r/planner/core/casetest/rule/rule_result_reorder.result index ab2e846c823b0..d8fe13371457d 100644 --- a/tests/integrationtest/r/planner/core/casetest/rule/rule_result_reorder.result +++ b/tests/integrationtest/r/planner/core/casetest/rule/rule_result_reorder.result @@ -77,11 +77,10 @@ Projection 3333.33 root planner__core__casetest__rule__rule_result_reorder.t.b └─TableRangeScan 3333.33 cop[tikv] table:t range:(0,+inf], keep order:true, stats:pseudo explain FORMAT='brief' select b from t where a>0 limit 1; id estRows task access object operator info -Projection 1.00 root planner__core__casetest__rule__rule_result_reorder.t.b -└─Limit 1.00 root offset:0, count:1 - └─TableReader 1.00 root data:Limit - └─Limit 1.00 cop[tikv] offset:0, count:1 - └─TableRangeScan 1.00 cop[tikv] table:t range:(0,+inf], keep order:true, stats:pseudo +Limit 1.00 root offset:0, count:1 +└─TableReader 1.00 root data:Limit + └─Limit 1.00 cop[tikv] offset:0, count:1 + └─TableRangeScan 1.00 cop[tikv] table:t range:(0,+inf], keep order:true, stats:pseudo set tidb_enable_ordered_result_mode=1; drop table if exists t; create table t (a int primary key, b int, c int, key(b)); diff --git a/tests/integrationtest/r/planner/core/issuetest/planner_issue.result b/tests/integrationtest/r/planner/core/issuetest/planner_issue.result index d1e8d6bc1830c..30be83825bc85 100644 --- a/tests/integrationtest/r/planner/core/issuetest/planner_issue.result +++ b/tests/integrationtest/r/planner/core/issuetest/planner_issue.result @@ -12,13 +12,12 @@ KEY 19dc3c2d (57fd8d09) ) ENGINE=InnoDB DEFAULT CHARSET=ascii COLLATE=ascii_bin COMMENT='320f8401'; explain select /*+ use_index_merge( `aa311c3c` ) */ `aa311c3c`.`43b06e99` as r0 , `aa311c3c`.`6302d8ac` as r1 from `aa311c3c` where `aa311c3c`.`b80b3746` = 3 or not( `aa311c3c`.`57fd8d09` >= '2008' ) order by r0,r1 limit 95; id estRows task access object operator info -Projection_7 95.00 root planner__core__issuetest__planner_issue.aa311c3c.43b06e99, planner__core__issuetest__planner_issue.aa311c3c.6302d8ac -└─TopN_9 95.00 root planner__core__issuetest__planner_issue.aa311c3c.43b06e99, planner__core__issuetest__planner_issue.aa311c3c.6302d8ac, offset:0, count:95 - └─IndexMerge_17 95.00 root type: union - ├─IndexRangeScan_13(Build) 10.00 cop[tikv] table:aa311c3c, index:464b386e(b80b3746) range:[3,3], keep order:false, stats:pseudo - ├─IndexRangeScan_14(Build) 3323.33 cop[tikv] table:aa311c3c, index:3080c821(57fd8d09, 43b06e99, b80b3746) range:[-inf,2008), keep order:false, stats:pseudo - └─TopN_16(Probe) 95.00 cop[tikv] planner__core__issuetest__planner_issue.aa311c3c.43b06e99, planner__core__issuetest__planner_issue.aa311c3c.6302d8ac, offset:0, count:95 - └─TableRowIDScan_15 3330.01 cop[tikv] table:aa311c3c keep order:false, stats:pseudo +TopN_9 95.00 root planner__core__issuetest__planner_issue.aa311c3c.43b06e99, planner__core__issuetest__planner_issue.aa311c3c.6302d8ac, offset:0, count:95 +└─IndexMerge_17 95.00 root type: union + ├─IndexRangeScan_13(Build) 10.00 cop[tikv] table:aa311c3c, index:464b386e(b80b3746) range:[3,3], keep order:false, stats:pseudo + ├─IndexRangeScan_14(Build) 3323.33 cop[tikv] table:aa311c3c, index:3080c821(57fd8d09, 43b06e99, b80b3746) range:[-inf,2008), keep order:false, stats:pseudo + └─TopN_16(Probe) 95.00 cop[tikv] planner__core__issuetest__planner_issue.aa311c3c.43b06e99, planner__core__issuetest__planner_issue.aa311c3c.6302d8ac, offset:0, count:95 + └─TableRowIDScan_15 3330.01 cop[tikv] table:aa311c3c keep order:false, stats:pseudo CREATE TABLE t1(id int,col1 varchar(10),col2 varchar(10),col3 varchar(10)); CREATE TABLE t2(id int,col1 varchar(10),col2 varchar(10),col3 varchar(10)); INSERT INTO t1 values(1,NULL,NULL,null),(2,NULL,NULL,null),(3,NULL,NULL,null); @@ -111,15 +110,14 @@ create table tbl_39(col_239 year(4) not null default '2009', primary key(col_239 insert into tbl_39 values (1994),(1995),(1996),(1997); explain select /*+ use_index_merge( tbl_39) */ col_239 from tbl_39 where not( tbl_39.col_239 not in ( '1994' ) ) and tbl_39.col_239 not in ( '2004' , '2010' , '2010' ) or not( tbl_39.col_239 <= '1996' ) and not( tbl_39.col_239 between '2026' and '2011' ) order by tbl_39.col_239 limit 382; id estRows task access object operator info -Projection_8 382.00 root planner__core__issuetest__planner_issue.tbl_39.col_239 -└─Limit_15 382.00 root offset:0, count:382 - └─UnionScan_23 382.00 root or(and(not(not(eq(planner__core__issuetest__planner_issue.tbl_39.col_239, 1994))), not(in(planner__core__issuetest__planner_issue.tbl_39.col_239, 2004, 2010, 2010))), and(not(le(planner__core__issuetest__planner_issue.tbl_39.col_239, 1996)), not(and(ge(cast(planner__core__issuetest__planner_issue.tbl_39.col_239, double UNSIGNED BINARY), 2026), le(cast(planner__core__issuetest__planner_issue.tbl_39.col_239, double UNSIGNED BINARY), 2011))))) - └─IndexMerge_29 382.00 root type: union - ├─Selection_25(Build) 0.05 cop[tikv] not(in(planner__core__issuetest__planner_issue.tbl_39.col_239, 2004, 2010, 2010)) - │ └─IndexRangeScan_24 0.14 cop[tikv] table:tbl_39, index:PRIMARY(col_239) range:[1994,1994], keep order:true, stats:pseudo - ├─Selection_27(Build) 458.26 cop[tikv] or(lt(cast(planner__core__issuetest__planner_issue.tbl_39.col_239, double UNSIGNED BINARY), 2026), gt(cast(planner__core__issuetest__planner_issue.tbl_39.col_239, double UNSIGNED BINARY), 2011)) - │ └─IndexRangeScan_26 477.36 cop[tikv] table:tbl_39, index:idx_223(col_239) range:(1996,+inf], keep order:true, stats:pseudo - └─TableRowIDScan_28(Probe) 382.00 cop[tikv] table:tbl_39 keep order:false, stats:pseudo +Limit_15 382.00 root offset:0, count:382 +└─UnionScan_23 382.00 root or(and(not(not(eq(planner__core__issuetest__planner_issue.tbl_39.col_239, 1994))), not(in(planner__core__issuetest__planner_issue.tbl_39.col_239, 2004, 2010, 2010))), and(not(le(planner__core__issuetest__planner_issue.tbl_39.col_239, 1996)), not(and(ge(cast(planner__core__issuetest__planner_issue.tbl_39.col_239, double UNSIGNED BINARY), 2026), le(cast(planner__core__issuetest__planner_issue.tbl_39.col_239, double UNSIGNED BINARY), 2011))))) + └─IndexMerge_29 382.00 root type: union + ├─Selection_25(Build) 0.05 cop[tikv] not(in(planner__core__issuetest__planner_issue.tbl_39.col_239, 2004, 2010, 2010)) + │ └─IndexRangeScan_24 0.14 cop[tikv] table:tbl_39, index:PRIMARY(col_239) range:[1994,1994], keep order:true, stats:pseudo + ├─Selection_27(Build) 458.26 cop[tikv] or(lt(cast(planner__core__issuetest__planner_issue.tbl_39.col_239, double UNSIGNED BINARY), 2026), gt(cast(planner__core__issuetest__planner_issue.tbl_39.col_239, double UNSIGNED BINARY), 2011)) + │ └─IndexRangeScan_26 477.36 cop[tikv] table:tbl_39, index:idx_223(col_239) range:(1996,+inf], keep order:true, stats:pseudo + └─TableRowIDScan_28(Probe) 382.00 cop[tikv] table:tbl_39 keep order:false, stats:pseudo select /*+ use_index_merge( tbl_39) */ col_239 from tbl_39 where not( tbl_39.col_239 not in ( '1994' ) ) and tbl_39.col_239 not in ( '2004' , '2010' , '2010' ) or not( tbl_39.col_239 <= '1996' ) and not( tbl_39.col_239 between '2026' and '2011' ) order by tbl_39.col_239 limit 382; col_239 1994 diff --git a/tests/integrationtest/r/select.result b/tests/integrationtest/r/select.result index 6912f6a7e3ad9..8215d8d6e0f84 100644 --- a/tests/integrationtest/r/select.result +++ b/tests/integrationtest/r/select.result @@ -316,13 +316,12 @@ count(a) 1 explain format = 'brief' select count(a) from t where b>0 group by a, b order by a limit 1; id estRows task access object operator info -Projection 1.00 root Column#5->Column#6 -└─Limit 1.00 root offset:0, count:1 - └─StreamAgg 1.00 root group by:select.t.a, select.t.b, funcs:count(Column#16)->Column#5, funcs:firstrow(select.t.a)->select.t.a - └─IndexReader 1.00 root index:StreamAgg - └─StreamAgg 1.00 cop[tikv] group by:select.t.a, select.t.b, funcs:count(select.t.a)->Column#16 - └─Selection 1.25 cop[tikv] gt(select.t.b, 0) - └─IndexFullScan 3.75 cop[tikv] table:t, index:idx(a, b, c) keep order:true, stats:pseudo +Limit 1.00 root offset:0, count:1 +└─StreamAgg 1.00 root group by:select.t.a, select.t.b, funcs:count(Column#16)->Column#5, funcs:firstrow(select.t.a)->select.t.a + └─IndexReader 1.00 root index:StreamAgg + └─StreamAgg 1.00 cop[tikv] group by:select.t.a, select.t.b, funcs:count(select.t.a)->Column#16 + └─Selection 1.25 cop[tikv] gt(select.t.b, 0) + └─IndexFullScan 3.75 cop[tikv] table:t, index:idx(a, b, c) keep order:true, stats:pseudo select count(a) from t where b>0 group by a, b order by a limit 1; count(a) 3