diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java index 51dacb59e83110..42f1a3e28292f2 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java @@ -7847,7 +7847,7 @@ public LogicalPlan visitShowTabletsFromTable(DorisParser.ShowTabletsFromTableCon if (ctx.sortClause() != null) { orderKeys = visit(ctx.sortClause().sortItem(), OrderKey.class); } - long limit = 0; + long limit = -1; long offset = 0; if (ctx.limitClause() != null) { limit = ctx.limitClause().limit != null diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowTabletsFromTableCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowTabletsFromTableCommand.java index a844455e9326aa..71f01a1f5ab3a0 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowTabletsFromTableCommand.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowTabletsFromTableCommand.java @@ -27,8 +27,6 @@ import org.apache.doris.catalog.Partition; import org.apache.doris.catalog.Replica; import org.apache.doris.catalog.ScalarType; -import org.apache.doris.catalog.info.PartitionNamesInfo; -import org.apache.doris.catalog.info.TableNameInfo; import org.apache.doris.common.AnalysisException; import org.apache.doris.common.ErrorCode; import org.apache.doris.common.ErrorReport; @@ -37,6 +35,8 @@ import org.apache.doris.common.util.ListComparator; import org.apache.doris.common.util.OrderByPair; import org.apache.doris.common.util.Util; +import org.apache.doris.info.PartitionNamesInfo; +import org.apache.doris.info.TableNameInfo; import org.apache.doris.mysql.privilege.PrivPredicate; import org.apache.doris.nereids.analyzer.UnboundSlot; import org.apache.doris.nereids.properties.OrderKey; @@ -48,6 +48,7 @@ import org.apache.doris.nereids.trees.plans.PlanType; import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; import org.apache.doris.nereids.util.ExpressionUtils; +import org.apache.doris.nereids.util.RecordPickerUtils; import org.apache.doris.qe.ConnectContext; import org.apache.doris.qe.ShowResultSet; import org.apache.doris.qe.ShowResultSetMetaData; @@ -57,9 +58,9 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Optional; /** * ShowTabletsFromTableCommand @@ -69,7 +70,7 @@ public class ShowTabletsFromTableCommand extends ShowCommand { private PartitionNamesInfo partitionNames; private Expression whereClause; private List orderKeys; - private long limit = 0; + private long limit = -1; private long offset = 0; private long version; @@ -105,7 +106,7 @@ public void validate(ConnectContext ctx) throws UserException { ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "SHOW TABLETS"); } - dbTableName.analyze(ctx.getNameSpaceContext()); + dbTableName.analyze(ctx); Util.prohibitExternalCatalog(dbTableName.getCtl(), this.getClass().getSimpleName()); if (partitionNames != null) { @@ -201,13 +202,12 @@ public ShowResultSet doRun(ConnectContext ctx, StmtExecutor executor) throws Exc OlapTable olapTable = db.getOlapTableOrAnalysisException(dbTableName.getTbl()); olapTable.readLock(); try { - long sizeLimit = -1; + Optional sizeLimit = Optional.empty(); if (offset > 0 && limit > 0) { - sizeLimit = offset + limit; + sizeLimit = Optional.of((int) (offset + limit)); } else if (limit > 0) { - sizeLimit = limit; + sizeLimit = Optional.of((int) limit); } - boolean stop = false; Collection partitions = new ArrayList(); if (partitionNames != null) { List paNames = partitionNames.getPartitionNames(); @@ -225,44 +225,30 @@ public ShowResultSet doRun(ConnectContext ctx, StmtExecutor executor) throws Exc } List> tabletInfos = new ArrayList<>(); for (Partition partition : partitions) { - if (stop) { - break; - } for (MaterializedIndex index : partition.getMaterializedIndices(IndexExtState.ALL)) { TabletsProcDir procDir = new TabletsProcDir(olapTable, index); tabletInfos.addAll(procDir.fetchComparableResult( version, backendId, replicaState)); - if (sizeLimit > -1 && tabletInfos.size() >= sizeLimit) { - stop = true; - break; - } } } - if (offset >= tabletInfos.size()) { - tabletInfos.clear(); + // order by + ListComparator> comparator; + if (orderByPairs != null) { + OrderByPair[] orderByPairArr = new OrderByPair[orderByPairs.size()]; + comparator = new ListComparator<>(orderByPairs.toArray(orderByPairArr)); } else { - // order by - ListComparator> comparator = null; - if (orderByPairs != null) { - OrderByPair[] orderByPairArr = new OrderByPair[orderByPairs.size()]; - comparator = new ListComparator<>(orderByPairs.toArray(orderByPairArr)); - } else { - // order by tabletId, replicaId - comparator = new ListComparator<>(0, 1); - } - Collections.sort(tabletInfos, comparator); - if (sizeLimit > -1) { - tabletInfos = tabletInfos.subList((int) offset, - Math.min((int) sizeLimit, tabletInfos.size())); - } - - for (List tabletInfo : tabletInfos) { - List oneTablet = new ArrayList(tabletInfo.size()); - for (Comparable column : tabletInfo) { - oneTablet.add(column.toString()); - } - rows.add(oneTablet); + // order by tabletId, replicaId + comparator = new ListComparator<>(0, 1); + } + List> orderedTableInfos = + RecordPickerUtils.getQualifiedRecords(tabletInfos, comparator, sizeLimit); + int resultOffset = (int) Math.min(offset, orderedTableInfos.size()); + for (List tabletInfo : orderedTableInfos.subList(resultOffset, orderedTableInfos.size())) { + List oneTablet = new ArrayList(tabletInfo.size()); + for (Comparable column : tabletInfo) { + oneTablet.add(column.toString()); } + rows.add(oneTablet); } } finally { olapTable.readUnlock(); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/RecordPickerUtils.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/RecordPickerUtils.java new file mode 100644 index 00000000000000..e5f102dc4a3c09 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/RecordPickerUtils.java @@ -0,0 +1,45 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.util; + +import org.apache.doris.common.util.ListComparator; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * Record Picker Util. + */ +public class RecordPickerUtils { + /** + * Qualifies the record(s) that are available to be picked in the Query / Command's result. + */ + public static List> getQualifiedRecords( + List> comparables, ListComparator> comparator, Optional k) { + int limit = k.orElse(comparables.size()); + if (comparables.isEmpty()) { + return new ArrayList<>(); + } + + //Collections.sort(comparables, comparator); + comparables.sort(comparator); + return new ArrayList<>(comparables.subList(0, Math.min(limit, comparables.size()))); + } + +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowTabletsFromTableCommandTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowTabletsFromTableCommandTest.java index 17613cd057ce02..5d1cf9960a45af 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowTabletsFromTableCommandTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowTabletsFromTableCommandTest.java @@ -27,6 +27,7 @@ import org.apache.doris.mysql.privilege.AccessControllerManager; import org.apache.doris.mysql.privilege.PrivPredicate; import org.apache.doris.nereids.analyzer.UnboundSlot; +import org.apache.doris.nereids.parser.NereidsParser; import org.apache.doris.nereids.properties.OrderKey; import org.apache.doris.nereids.trees.expressions.EqualTo; import org.apache.doris.nereids.trees.expressions.Expression; @@ -50,6 +51,19 @@ public class ShowTabletsFromTableCommandTest extends TestWithFeService { private Env env; private AccessControllerManager accessControllerManager; + @Test + void testLimitClauseParsing() { + ShowTabletsFromTableCommand withoutLimit = (ShowTabletsFromTableCommand) new NereidsParser() + .parseSingle("SHOW TABLETS FROM test_table ORDER BY LocalDataSize DESC, TabletId ASC"); + ShowTabletsFromTableCommand withZeroLimit = (ShowTabletsFromTableCommand) new NereidsParser() + .parseSingle("SHOW TABLETS FROM test_table ORDER BY LocalDataSize DESC, TabletId ASC LIMIT 0"); + long withoutLimitValue = Deencapsulation.getField(withoutLimit, "limit"); + long zeroLimitValue = Deencapsulation.getField(withZeroLimit, "limit"); + + Assertions.assertEquals(-1L, withoutLimitValue); + Assertions.assertEquals(0L, zeroLimitValue); + } + private void runBefore() throws IOException { connectContext = createDefaultCtx(); env = Env.getCurrentEnv(); diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/RecordPickerUtilsTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/RecordPickerUtilsTest.java new file mode 100644 index 00000000000000..47306c7b34cc5d --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/RecordPickerUtilsTest.java @@ -0,0 +1,74 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.util; + +import org.apache.doris.common.util.ListComparator; +import org.apache.doris.common.util.OrderByPair; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +class RecordPickerUtilsTest { + + @Test + void testZeroLimitReturnsNoRecords() { + List> records = Arrays.asList( + createTabletRecord(2L, 100L), + createTabletRecord(1L, 100L)); + ListComparator> comparator = new ListComparator<>( + new OrderByPair(8, true), + new OrderByPair(0, false)); + + List> actual = RecordPickerUtils.getQualifiedRecords( + records, comparator, Optional.of(0)); + + Assertions.assertTrue(actual.isEmpty()); + } + + @Test + void testMultipleOrderByColumnsWithDifferentDirections() { + List> records = Arrays.asList( + createTabletRecord(3L, 200L), + createTabletRecord(2L, 100L), + createTabletRecord(1L, 200L), + createTabletRecord(3L, 100L), + createTabletRecord(2L, 200L), + createTabletRecord(1L, 100L)); + ListComparator> comparator = new ListComparator<>( + new OrderByPair(8, true), + new OrderByPair(0, false)); + + List> actual = RecordPickerUtils.getQualifiedRecords( + records, comparator, Optional.of(5)); + + Assertions.assertEquals(Arrays.asList( + createTabletRecord(1L, 200L), + createTabletRecord(2L, 200L), + createTabletRecord(3L, 200L), + createTabletRecord(1L, 100L), + createTabletRecord(2L, 100L)), actual); + } + + private List createTabletRecord(long tabletId, long localDataSize) { + return Arrays.asList(tabletId, 0L, 0L, 0L, 0L, 0L, 0L, 0L, localDataSize); + } +}