This is an automated email from the ASF dual-hosted git repository.
starocean999 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 8a12288134e [Enhancement] (nereids) implement ShowCopyCommand in
nereids (#51040)
8a12288134e is described below
commit 8a12288134e2d8054ac8cdcea038d587e77105c3
Author: yaoxiao <[email protected]>
AuthorDate: Thu May 22 10:19:15 2025 +0800
[Enhancement] (nereids) implement ShowCopyCommand in nereids (#51040)
---
.../antlr4/org/apache/doris/nereids/DorisParser.g4 | 4 +-
.../doris/nereids/parser/LogicalPlanBuilder.java | 37 ++++
.../trees/plans/commands/ShowCopyCommand.java | 233 +++++++++++++++++++++
.../trees/plans/visitor/CommandVisitor.java | 5 +
.../trees/plans/commands/ShowCopyCommandTest.java | 127 +++++++++++
5 files changed, 404 insertions(+), 2 deletions(-)
diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
index 8764f346bd8..4bfce104713 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
@@ -394,6 +394,8 @@ supportedShowStatement
| SHOW CATALOG RECYCLE BIN (WHERE expression)?
#showCatalogRecycleBin
| SHOW TABLET tabletId=INTEGER_VALUE
#showTabletId
| SHOW DICTIONARIES wildWhere?
#showDictionaries
+ | SHOW COPY ((FROM | IN) database=identifier)?
+ whereClause? sortClause? limitClause?
#showCopy
| SHOW QUERY STATS ((FOR database=identifier)
| (FROM tableName=multipartIdentifier (ALL VERBOSE?)?))?
#showQueryStats
;
@@ -478,8 +480,6 @@ unsupportedShowStatement
| SHOW BUILD INDEX ((FROM | IN) database=multipartIdentifier)?
wildWhere? sortClause? limitClause?
#showBuildIndex
| SHOW REPLICA STATUS FROM baseTableRef wildWhere?
#showReplicaStatus
- | SHOW COPY ((FROM | IN) database=multipartIdentifier)?
- whereClause? sortClause? limitClause?
#showCopy
| SHOW WARM UP JOB wildWhere?
#showWarmUpJob
;
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 c931653cf99..f3a70391cf2 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
@@ -686,6 +686,7 @@ import
org.apache.doris.nereids.trees.plans.commands.ShowColumnHistogramStatsCom
import org.apache.doris.nereids.trees.plans.commands.ShowConfigCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowConstraintsCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowConvertLSCCommand;
+import org.apache.doris.nereids.trees.plans.commands.ShowCopyCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowCreateCatalogCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowCreateDatabaseCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowCreateMTMVCommand;
@@ -7647,4 +7648,40 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
return new RefreshDictionaryCommand(dbName, dictName);
}
+
+ @Override
+ public LogicalPlan visitShowCopy(DorisParser.ShowCopyContext ctx) {
+ String dbName = null;
+ if (ctx.database != null) {
+ dbName = ctx.database.getText();
+ }
+
+ Expression whereClause = null;
+ if (ctx.whereClause() != null) {
+ whereClause = getExpression(ctx.whereClause().booleanExpression());
+ }
+
+ List<OrderKey> orderKeys = new ArrayList<>();
+ if (ctx.sortClause() != null) {
+ orderKeys = visit(ctx.sortClause().sortItem(), OrderKey.class);
+ }
+
+ long limit = -1L;
+ long offset = 0L;
+ if (ctx.limitClause() != null) {
+ limit = ctx.limitClause().limit != null
+ ? Long.parseLong(ctx.limitClause().limit.getText())
+ : 0;
+ if (limit < 0) {
+ throw new ParseException("Limit requires non-negative number",
ctx.limitClause());
+ }
+ offset = ctx.limitClause().offset != null
+ ? Long.parseLong(ctx.limitClause().offset.getText())
+ : 0;
+ if (offset < 0) {
+ throw new ParseException("Offset requires non-negative
number", ctx.limitClause());
+ }
+ }
+ return new ShowCopyCommand(dbName, orderKeys, whereClause, limit,
offset);
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowCopyCommand.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowCopyCommand.java
new file mode 100644
index 00000000000..bcaf6aeac77
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowCopyCommand.java
@@ -0,0 +1,233 @@
+// 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.trees.plans.commands;
+
+import org.apache.doris.analysis.RedirectStatus;
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.ScalarType;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.common.proc.LoadProcDir;
+import org.apache.doris.load.LoadJob.JobState;
+import org.apache.doris.nereids.analyzer.UnboundSlot;
+import org.apache.doris.nereids.properties.OrderKey;
+import org.apache.doris.nereids.trees.expressions.And;
+import org.apache.doris.nereids.trees.expressions.CompoundPredicate;
+import org.apache.doris.nereids.trees.expressions.EqualTo;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.Like;
+import org.apache.doris.nereids.trees.expressions.literal.StringLiteral;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.ShowResultSetMetaData;
+
+import com.google.common.base.Strings;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * ShowCopyCommand
+ */
+public class ShowCopyCommand extends ShowLoadCommand {
+
+ private String dbName;
+ private Expression whereClause;
+
+ public ShowCopyCommand(String dbName, List<OrderKey> orderByElements,
+ Expression where, long limit, long offset) {
+ super(where, orderByElements, limit, offset, dbName, false);
+ this.dbName = dbName;
+ this.whereClause = where;
+ }
+
+ @Override
+ protected boolean validate(ConnectContext ctx) throws AnalysisException {
+ if (Strings.isNullOrEmpty(dbName)) {
+ dbName = ctx.getDatabase();
+ if (Strings.isNullOrEmpty(dbName)) {
+ ErrorReport.reportAnalysisException(ErrorCode.ERR_NO_DB_ERROR);
+ }
+ }
+
+ // analyze where clause if not null
+ if (whereClause != null) {
+ if (whereClause instanceof CompoundPredicate) {
+ return analyzeCompoundPredicate(whereClause);
+ } else {
+ return analyzeSubPredicate(whereClause);
+ }
+ }
+ return true;
+ }
+
+ private boolean analyzeSubPredicate(Expression expr) throws
AnalysisException {
+ if (expr == null) {
+ return true;
+ }
+ return isWhereClauseValid(expr);
+ }
+
+ private boolean isWhereClauseValid(Expression expr) {
+ boolean hasLabel = false;
+ boolean hasState = false;
+ boolean hasCopyId = false;
+ boolean hasTableName = false;
+ boolean hasFile = false;
+
+ if (!((expr instanceof EqualTo) || (expr instanceof Like))) {
+ return false;
+ }
+
+ if (!(expr.child(0) instanceof UnboundSlot)) {
+ return false;
+ }
+
+ String leftKey = ((UnboundSlot) expr.child(0)).getName();
+
+ if (leftKey.equalsIgnoreCase("label")) {
+ hasLabel = true;
+ } else if (leftKey.equalsIgnoreCase("state")) {
+ hasState = true;
+ } else if (leftKey.equalsIgnoreCase("id")) {
+ hasCopyId = true;
+ } else if (leftKey.equalsIgnoreCase("TableName")) {
+ hasTableName = true;
+ } else if (leftKey.equalsIgnoreCase("files")) {
+ hasFile = true;
+ } else {
+ return false;
+ }
+
+ if (hasState && !(expr instanceof EqualTo)) {
+ return false;
+ }
+
+ if (hasLabel && expr instanceof EqualTo) {
+ isAccurateMatch = true;
+ }
+
+ if (hasCopyId && expr instanceof EqualTo) {
+ isCopyIdAccurateMatch = true;
+ }
+
+ if (hasTableName && expr instanceof EqualTo) {
+ isTableNameAccurateMatch = true;
+ }
+
+ if (hasFile && expr instanceof EqualTo) {
+ isFilesAccurateMatch = true;
+ }
+
+ // right child
+ if (!(expr.child(1) instanceof StringLiteral)) {
+ return false;
+ }
+
+ String value = ((StringLiteral) expr.child(1)).getStringValue();
+ if (Strings.isNullOrEmpty(value)) {
+ return false;
+ }
+
+ if (hasLabel && !isAccurateMatch && !value.contains("%")) {
+ value = "%" + value + "%";
+ }
+ if (hasCopyId && !isCopyIdAccurateMatch && !value.contains("%")) {
+ value = "%" + value + "%";
+ }
+ if (hasTableName && !isTableNameAccurateMatch && !value.contains("%"))
{
+ value = "%" + value + "%";
+ }
+ if (hasFile && !isFilesAccurateMatch && !value.contains("%")) {
+ value = "%" + value + "%";
+ }
+
+ if (hasLabel) {
+ labelValue = value;
+ } else if (hasState) {
+ stateValue = value.toUpperCase();
+
+ try {
+ JobState.valueOf(stateValue);
+ } catch (Exception e) {
+ return false;
+ }
+ } else if (hasCopyId) {
+ copyIdValue = value;
+ } else if (hasTableName) {
+ tableNameValue = value;
+ } else if (hasFile) {
+ fileValue = value;
+ }
+ return true;
+ }
+
+ private boolean analyzeCompoundPredicate(Expression expr) throws
AnalysisException {
+ List<Expression> children = new ArrayList<>();
+ analyzeCompoundPredicate(expr, children);
+ Set<String> names = new HashSet<>();
+ boolean result = true;
+ for (Expression expression : children) {
+ String name = ((UnboundSlot) expression.child(0)).getName();
+ if (names.contains(name)) {
+ throw new AnalysisException("column names on both sides of
operator AND should be diffrent");
+ }
+ names.add(name);
+ result = result && analyzeSubPredicate(expression);
+ if (!result) {
+ return result;
+ }
+ }
+ return result;
+ }
+
+ private void analyzeCompoundPredicate(Expression expr, List<Expression>
exprs) throws AnalysisException {
+ if (expr instanceof CompoundPredicate) {
+ CompoundPredicate cp = (CompoundPredicate) expr;
+ if (!(cp instanceof And)) {
+ throw new AnalysisException("Only allow compound predicate
with operator AND");
+ }
+ analyzeCompoundPredicate(expr.child(0), exprs);
+ analyzeCompoundPredicate(expr.child(1), exprs);
+ } else {
+ exprs.add(expr);
+ }
+ }
+
+ @Override
+ public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+ return visitor.visitShowCopyCommand(this, context);
+ }
+
+ @Override
+ public ShowResultSetMetaData getMetaData() {
+ ShowResultSetMetaData.Builder builder =
ShowResultSetMetaData.builder();
+ for (String title : LoadProcDir.COPY_TITLE_NAMES) {
+ builder.addColumn(new Column(title, ScalarType.createVarchar(30)));
+ }
+ return builder.build();
+ }
+
+ @Override
+ public RedirectStatus toRedirectStatus() {
+ return RedirectStatus.FORWARD_NO_SYNC;
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
index 3e63f3328ad..95be19629da 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
@@ -148,6 +148,7 @@ import
org.apache.doris.nereids.trees.plans.commands.ShowColumnHistogramStatsCom
import org.apache.doris.nereids.trees.plans.commands.ShowConfigCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowConstraintsCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowConvertLSCCommand;
+import org.apache.doris.nereids.trees.plans.commands.ShowCopyCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowCreateCatalogCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowCreateDatabaseCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowCreateMTMVCommand;
@@ -1166,6 +1167,10 @@ public interface CommandVisitor<R, C> {
return visitCommand(truncateTableCommand, context);
}
+ default R visitShowCopyCommand(ShowCopyCommand showCopyCommand, C context)
{
+ return visitCommand(showCopyCommand, context);
+ }
+
default R visitGrantRoleCommand(GrantRoleCommand grantRoleCommand, C
context) {
return visitCommand(grantRoleCommand, context);
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowCopyCommandTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowCopyCommandTest.java
new file mode 100644
index 00000000000..77859fa3bb5
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowCopyCommandTest.java
@@ -0,0 +1,127 @@
+// 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.trees.plans.commands;
+
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.util.OrderByPair;
+import org.apache.doris.nereids.analyzer.UnboundSlot;
+import org.apache.doris.nereids.properties.OrderKey;
+import org.apache.doris.nereids.trees.expressions.And;
+import org.apache.doris.nereids.trees.expressions.EqualTo;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.Like;
+import org.apache.doris.nereids.trees.expressions.Or;
+import org.apache.doris.nereids.trees.expressions.literal.StringLiteral;
+import org.apache.doris.utframe.TestWithFeService;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ShowCopyCommandTest extends TestWithFeService {
+ public static final ImmutableList<String> LOAD_TITLE_NAMES = new
ImmutableList.Builder<String>()
+ .add("JobId").add("Label").add("State").add("Progress")
+
.add("Type").add("EtlInfo").add("TaskInfo").add("ErrorMsg").add("CreateTime")
+
.add("EtlStartTime").add("EtlFinishTime").add("LoadStartTime").add("LoadFinishTime")
+
.add("URL").add("JobDetails").add("TransactionId").add("ErrorTablets").add("User").add("Comment")
+ .build();
+
+ @Override
+ protected void runBeforeAll() throws Exception {
+ createDatabase("test");
+ }
+
+ @Test
+ public void testValidate() throws AnalysisException {
+ // test where is null but db is not null
+ ShowCopyCommand command = new ShowCopyCommand("test", null, null, -1,
-1);
+ Assertions.assertTrue(command.validate(connectContext));
+
+ // test where is not null
+ Expression where1 = new EqualTo(new
UnboundSlot(Lists.newArrayList("LABEL")),
+ new StringLiteral("xxx"));
+ command = new ShowCopyCommand("test", null, where1, -1, -1);
+ Assertions.assertTrue(command.validate(connectContext));
+ Assertions.assertTrue(command.isAccurateMatch());
+
+ // test where is And, child(0) or child(1) is EqualTo
+ Expression equalTo1 = new EqualTo(new
UnboundSlot(Lists.newArrayList("STATE")),
+ new StringLiteral("PENDING"));
+ Expression equalTo2 = new EqualTo(new
UnboundSlot(Lists.newArrayList("LABEL")),
+ new StringLiteral("test_label"));
+ Expression where3 = new And(equalTo1, equalTo2);
+ command = new ShowCopyCommand("test", null, where3, -1, -1);
+ Assertions.assertTrue(command.validate(connectContext));
+
+ // test where is And, child(0) or child(1) is ComparisonPredicate
+ Expression equalTo = new EqualTo(new
UnboundSlot(Lists.newArrayList("STATE")),
+ new StringLiteral("PENDING"));
+ Expression like = new Like(new
UnboundSlot(Lists.newArrayList("LABEL")),
+ new StringLiteral("xxx%"));
+ Expression where4 = new And(equalTo, like);
+ command = new ShowCopyCommand("test", null, where4, -1, -1);
+ Assertions.assertTrue(command.validate(connectContext));
+
+ // test where is Or
+ Expression equalTo4 = new EqualTo(new
UnboundSlot(Lists.newArrayList("STATE")),
+ new StringLiteral("STATE"));
+ Expression equalTo5 = new EqualTo(new
UnboundSlot(Lists.newArrayList("LABEL")),
+ new StringLiteral("xxx"));
+ Expression where5 = new Or(equalTo4, equalTo5);
+ command = new ShowCopyCommand("test", null, where5, -1, -1);
+ ShowCopyCommand command1 = command;
+ Assertions.assertThrows(AnalysisException.class, () ->
command1.validate(connectContext));
+ }
+
+ @Test
+ public void testProcessOrderBy() throws AnalysisException {
+ UnboundSlot key = new UnboundSlot(Lists.newArrayList("LABEL"));
+ List<OrderKey> orderKeys = Lists.newArrayList(new OrderKey(key, true,
true));
+ ShowCopyCommand command = new ShowCopyCommand("test", orderKeys, null,
-1, -1);
+ ArrayList<OrderByPair> orderByPairs =
command.getOrderByPairs(orderKeys, LOAD_TITLE_NAMES);
+ OrderByPair op = orderByPairs.get(0);
+ Assertions.assertFalse(op.isDesc());
+ Assertions.assertEquals(1, op.getIndex());
+ }
+
+ @Test
+ public void testApplyLimit() {
+ UnboundSlot key = new UnboundSlot(Lists.newArrayList("LABEL"));
+ List<OrderKey> orderKeys = Lists.newArrayList(new OrderKey(key, true,
true));
+ long limit = 1;
+ long offset = 1;
+ ShowCopyCommand command = new ShowCopyCommand("test", orderKeys, null,
limit, offset);
+ List<List<String>> rows = new ArrayList<>();
+ List<String> row1 = new ArrayList<>();
+ List<String> row2 = new ArrayList<>();
+ row1.add("a");
+ row1.add("b");
+ row2.add("x");
+ row2.add("y");
+ rows.add(row1);
+ rows.add(row2);
+ rows = command.applyLimit(limit, offset, rows);
+ Assertions.assertEquals(1, rows.size());
+ Assertions.assertEquals("x", rows.get(0).get(0));
+ Assertions.assertEquals("y", rows.get(0).get(1));
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]