This is an automated email from the ASF dual-hosted git repository. morningman pushed a commit to branch dev-1.0.1 in repository https://gitbox.apache.org/repos/asf/incubator-doris.git
commit 5a10377003a98880a6d4c035d8d052cba9d69b7a Author: wunan1210 <wunan1...@gmail.com> AuthorDate: Tue Mar 15 11:41:59 2022 +0800 [improvment] show export support label like (#8202) using `show export where label like 'xxx%'` to list more results. --- .../Data Manipulation/SHOW EXPORT.md | 9 +- .../Data Manipulation/SHOW EXPORT.md | 9 +- .../org/apache/doris/analysis/ShowExportStmt.java | 112 ++++++++------------- .../apache/doris/common/proc/ExportProcNode.java | 2 +- .../main/java/org/apache/doris/load/ExportMgr.java | 22 +++- .../java/org/apache/doris/qe/ShowExecutor.java | 2 +- .../apache/doris/analysis/ShowExportStmtTest.java | 89 ++++++++++++++++ .../apache/doris/load/loadv2/ExportMgrTest.java | 99 ++++++++++++++++++ 8 files changed, 261 insertions(+), 83 deletions(-) diff --git a/docs/en/sql-reference/sql-statements/Data Manipulation/SHOW EXPORT.md b/docs/en/sql-reference/sql-statements/Data Manipulation/SHOW EXPORT.md index df79a8f..aba5fbd 100644 --- a/docs/en/sql-reference/sql-statements/Data Manipulation/SHOW EXPORT.md +++ b/docs/en/sql-reference/sql-statements/Data Manipulation/SHOW EXPORT.md @@ -34,7 +34,7 @@ Grammar: WHERE [ID = your_job_id] [STATE = ["PENDING"|"EXPORTING"|"FINISHED"|"CANCELLED"]] - [LABEL = "your_label"] + [LABEL [ = "your_label" | LIKE "label_matcher"]] ] [ORDER BY ...] [LIMIT limit]; @@ -55,12 +55,15 @@ Explain: 3. Show the export task of the specified db, state is "exporting" and sorted in descending order by StartTime SHOW EXPORT FROM example_db WHERE STATE = "exporting" ORDER BY StartTime DESC; -4. Show the export task of specifying dB and job_id +4. Show the export task of specifying DB and job_id SHOW EXPORT FROM example_db WHERE ID = job_id; -5. Show the export task of specifying dB and label +5. Show the export task of specifying DB and label SHOW EXPORT FROM example_db WHERE LABEL = "mylabel"; +6. Show the export task of specifying DB and label prefix is "labelprefix" + SHOW EXPORT FROM example_db WHERE LABEL LIKE "labelprefix%"; + ## keyword SHOW,EXPORT diff --git a/docs/zh-CN/sql-reference/sql-statements/Data Manipulation/SHOW EXPORT.md b/docs/zh-CN/sql-reference/sql-statements/Data Manipulation/SHOW EXPORT.md index 327741a..c29b976 100644 --- a/docs/zh-CN/sql-reference/sql-statements/Data Manipulation/SHOW EXPORT.md +++ b/docs/zh-CN/sql-reference/sql-statements/Data Manipulation/SHOW EXPORT.md @@ -34,7 +34,7 @@ under the License. WHERE [ID = your_job_id] [STATE = ["PENDING"|"EXPORTING"|"FINISHED"|"CANCELLED"]] - [LABEL = your_label] + [LABEL [ = "your_label" | LIKE "label_matcher"]] ] [ORDER BY ...] [LIMIT limit]; @@ -55,12 +55,15 @@ under the License. 3. 展示指定 db 的导出任务,state 为 "exporting", 并按 StartTime 降序排序 SHOW EXPORT FROM example_db WHERE STATE = "exporting" ORDER BY StartTime DESC; - 4. 展示指定db,指定job_id的导出任务 + 4. 展示指定 db,指定 job_id 的导出任务 SHOW EXPORT FROM example_db WHERE ID = job_id; - 5. 展示指定db,指定label的导出任务 + 5. 展示指定 db,指定 label 的导出任务 SHOW EXPORT FROM example_db WHERE LABEL = "mylabel"; + 6. 展示指定 db,label 中前缀是 labelprefix 的导出任务 + SHOW EXPORT FROM example_db WHERE LABEL LIKE "labelprefix%"; + ## keyword SHOW,EXPORT diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowExportStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowExportStmt.java index 5810386..3face77 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowExportStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowExportStmt.java @@ -46,12 +46,13 @@ public class ShowExportStmt extends ShowStmt { private static final Logger LOG = LogManager.getLogger(ShowExportStmt.class); private String dbName; - private Expr whereClause; - private LimitElement limitElement; - private List<OrderByElement> orderByElements; + private final Expr whereClause; + private final LimitElement limitElement; + private final List<OrderByElement> orderByElements; private long jobId = 0; private String label = null; + private boolean isLabelUseLike = false; private String stateValue = null; private JobState jobState; @@ -95,6 +96,10 @@ public class ShowExportStmt extends ShowStmt { return label; } + public boolean isLabelUseLike() { + return isLabelUseLike; + } + @Override public void analyze(Analyzer analyzer) throws AnalysisException, UserException { super.analyze(analyzer); @@ -114,7 +119,7 @@ public class ShowExportStmt extends ShowStmt { // order by if (orderByElements != null && !orderByElements.isEmpty()) { - orderByPairs = new ArrayList<OrderByPair>(); + orderByPairs = new ArrayList<>(); for (OrderByElement orderByElement : orderByElements) { if (!(orderByElement.getExpr() instanceof SlotRef)) { throw new AnalysisException("Should order by column"); @@ -132,82 +137,47 @@ public class ShowExportStmt extends ShowStmt { return; } - boolean valid = true; - boolean hasJobId = false; - boolean hasState = false; - boolean hasLabel = false; - - CHECK: { - // check predicate type - if (whereExpr instanceof BinaryPredicate) { - BinaryPredicate binaryPredicate = (BinaryPredicate) whereExpr; - if (binaryPredicate.getOp() != Operator.EQ) { - valid = false; - break CHECK; - } - } else { - valid = false; - break CHECK; - } - - // left child - if (!(whereExpr.getChild(0) instanceof SlotRef)) { - valid = false; - break CHECK; - } - String leftKey = ((SlotRef) whereExpr.getChild(0)).getColumnName(); - if (leftKey.equalsIgnoreCase("id")) { - hasJobId = true; - } else if (leftKey.equalsIgnoreCase("state")) { - hasState = true; - } else if (leftKey.equalsIgnoreCase("label")) { - hasLabel = true; - } else { - valid = false; - break CHECK; - } - - // right child - if (hasState) { - if (!(whereExpr.getChild(1) instanceof StringLiteral)) { - valid = false; - break CHECK; + boolean valid = false; + + // enumerate all possible conditions + if (whereExpr.getChild(0) instanceof SlotRef) { + String leftKey = ((SlotRef) whereExpr.getChild(0)).getColumnName().toLowerCase(); + + if (whereExpr instanceof BinaryPredicate && ((BinaryPredicate) whereExpr).getOp() == Operator.EQ) { + if ("id".equals(leftKey) && whereExpr.getChild(1) instanceof IntLiteral) { + jobId = ((IntLiteral) whereExpr.getChild(1)).getLongValue(); + valid = true; + + } else if ("state".equals(leftKey) && whereExpr.getChild(1) instanceof StringLiteral) { + String value = whereExpr.getChild(1).getStringValue(); + if (!Strings.isNullOrEmpty(value)) { + stateValue = value.toUpperCase(); + try { + jobState = JobState.valueOf(stateValue); + valid = true; + } catch (IllegalArgumentException e) { + LOG.warn("illegal state argument in export stmt. stateValue={}, error={}", stateValue, e); + } + } + + } else if ("label".equals(leftKey) && whereExpr.getChild(1) instanceof StringLiteral) { + label = whereExpr.getChild(1).getStringValue(); + valid = true; } - String value = ((StringLiteral) whereExpr.getChild(1)).getStringValue(); - if (Strings.isNullOrEmpty(value)) { - valid = false; - break CHECK; - } - - stateValue = value.toUpperCase(); - - try { - jobState = JobState.valueOf(stateValue); - } catch (IllegalArgumentException e) { - LOG.warn("illegal state argument in export stmt. stateValue={}, error={}", stateValue, e); - valid = false; - break CHECK; - } - } else if (hasJobId) { - if (!(whereExpr.getChild(1) instanceof IntLiteral)) { - valid = false; - break CHECK; - } - jobId = ((IntLiteral) whereExpr.getChild(1)).getLongValue(); - } else if (hasLabel) { - if (!(whereExpr.getChild(1) instanceof StringLiteral)) { - valid = false; - break CHECK; + } else if (whereExpr instanceof LikePredicate && ((LikePredicate) whereExpr).getOp() == LikePredicate.Operator.LIKE) { + if ("label".equals(leftKey) && whereExpr.getChild(1) instanceof StringLiteral) { + label = whereExpr.getChild(1).getStringValue(); + isLabelUseLike = true; + valid = true; } - label = ((StringLiteral) whereExpr.getChild(1)).getStringValue(); } } if (!valid) { throw new AnalysisException("Where clause should looks like below: " + " ID = $your_job_id, or STATE = \"PENDING|EXPORTING|FINISHED|CANCELLED\", " + - "or label=\"xxx\""); + "or LABEL = \"xxx\" or LABEL like \"xxx%\""); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/proc/ExportProcNode.java b/fe/fe-core/src/main/java/org/apache/doris/common/proc/ExportProcNode.java index bc13ca0..3bb816e 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/common/proc/ExportProcNode.java +++ b/fe/fe-core/src/main/java/org/apache/doris/common/proc/ExportProcNode.java @@ -57,7 +57,7 @@ public class ExportProcNode implements ProcNodeInterface { BaseProcResult result = new BaseProcResult(); result.setNames(TITLE_NAMES); - List<List<String>> jobInfos = exportMgr.getExportJobInfosByIdOrState(db.getId(), 0, "", null, null, LIMIT); + List<List<String>> jobInfos = exportMgr.getExportJobInfosByIdOrState(db.getId(), 0, "",false, null, null, LIMIT); result.setRows(jobInfos); return result; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/load/ExportMgr.java b/fe/fe-core/src/main/java/org/apache/doris/load/ExportMgr.java index 96877f5..59a3b3c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/load/ExportMgr.java +++ b/fe/fe-core/src/main/java/org/apache/doris/load/ExportMgr.java @@ -21,9 +21,12 @@ import org.apache.doris.analysis.ExportStmt; import org.apache.doris.analysis.TableName; import org.apache.doris.catalog.Catalog; import org.apache.doris.catalog.Database; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.CaseSensibility; import org.apache.doris.common.Config; import org.apache.doris.common.FeConstants; import org.apache.doris.common.LabelAlreadyUsedException; +import org.apache.doris.common.PatternMatcher; import org.apache.doris.common.util.ListComparator; import org.apache.doris.common.util.OrderByPair; import org.apache.doris.common.util.TimeUtils; @@ -126,11 +129,16 @@ public class ExportMgr { // NOTE: jobid and states may both specified, or only one of them, or neither public List<List<String>> getExportJobInfosByIdOrState( - long dbId, long jobId, String label, Set<ExportJob.JobState> states, - ArrayList<OrderByPair> orderByPairs, long limit) { + long dbId, long jobId, String label, boolean isLabelUseLike, Set<ExportJob.JobState> states, + ArrayList<OrderByPair> orderByPairs, long limit) throws AnalysisException { long resultNum = limit == -1L ? Integer.MAX_VALUE : limit; LinkedList<List<Comparable>> exportJobInfos = new LinkedList<List<Comparable>>(); + PatternMatcher matcher = null; + if (isLabelUseLike) { + matcher = PatternMatcher.createMysqlPattern(label, CaseSensibility.LABEL.getCaseSensibility()); + } + readLock(); try { int counter = 0; @@ -147,8 +155,14 @@ public class ExportMgr { continue; } - if (!Strings.isNullOrEmpty(label) && !jobLabel.equals(label)) { - continue; + if (!Strings.isNullOrEmpty(label)) { + if (!isLabelUseLike && !jobLabel.equals(label)) { + // use = but does not match + continue; + } else if (isLabelUseLike && !matcher.match(jobLabel)) { + // use like but does not match + continue; + } } // check auth diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java index 41688c7..fa983e8 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java @@ -1631,7 +1631,7 @@ public class ShowExecutor { states = Sets.newHashSet(state); } List<List<String>> infos = exportMgr.getExportJobInfosByIdOrState( - dbId, showExportStmt.getJobId(), showExportStmt.getLabel(), states, + dbId, showExportStmt.getJobId(), showExportStmt.getLabel(),showExportStmt.isLabelUseLike(), states, showExportStmt.getOrderByPairs(), showExportStmt.getLimit()); resultSet = new ShowResultSet(showExportStmt.getMetaData(), infos); diff --git a/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowExportStmtTest.java b/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowExportStmtTest.java new file mode 100644 index 0000000..b86f3a2 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowExportStmtTest.java @@ -0,0 +1,89 @@ +// 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.analysis; + +import org.apache.doris.analysis.BinaryPredicate.Operator; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.ExceptionChecker; +import org.apache.doris.common.UserException; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class ShowExportStmtTest { + private Analyzer analyzer; + + @Before + public void setUp() { + analyzer = AccessTestUtil.fetchAdminAnalyzer(true); + } + + @Test + public void testNormal() throws UserException { + ShowExportStmt stmt = new ShowExportStmt(null, null, null, null); + stmt.analyze(analyzer); + Assert.assertEquals("SHOW EXPORT FROM `testCluster:testDb`", stmt.toString()); + } + + @Test + public void testWhere() throws UserException { + ShowExportStmt stmt = new ShowExportStmt(null, null, null, null); + stmt.analyze(analyzer); + Assert.assertEquals("SHOW EXPORT FROM `testCluster:testDb`", stmt.toString()); + + SlotRef slotRef = new SlotRef(null, "label"); + StringLiteral stringLiteral = new StringLiteral("abc"); + BinaryPredicate binaryPredicate = new BinaryPredicate(Operator.EQ, slotRef, stringLiteral); + + stmt = new ShowExportStmt(null, binaryPredicate, null, new LimitElement(10)); + stmt.analyze(analyzer); + Assert.assertEquals("SHOW EXPORT FROM `testCluster:testDb` WHERE `label` = 'abc' LIMIT 10", stmt.toString()); + Assert.assertFalse(stmt.isLabelUseLike()); + + StringLiteral stringLiteralLike = new StringLiteral("ab%"); + LikePredicate likePredicate = new LikePredicate(LikePredicate.Operator.LIKE, slotRef, stringLiteralLike); + + stmt = new ShowExportStmt(null, likePredicate, null, new LimitElement(10)); + stmt.analyze(analyzer); + Assert.assertEquals("SHOW EXPORT FROM `testCluster:testDb` WHERE `label` LIKE 'ab%' LIMIT 10", stmt.toString()); + Assert.assertTrue(stmt.isLabelUseLike()); + + BinaryPredicate statePredicate = new BinaryPredicate(Operator.EQ, new SlotRef(null, "state"), new StringLiteral("PENDING")); + stmt = new ShowExportStmt(null, statePredicate, null, new LimitElement(10)); + stmt.analyze(analyzer); + Assert.assertEquals("SHOW EXPORT FROM `testCluster:testDb` WHERE `state` = 'PENDING' LIMIT 10", stmt.toString()); + } + + @Test + public void testInvalidWhereClause() { + //test: WHERE label="abc" AND id = 1; --> AnalysisException + SlotRef slotRef1 = new SlotRef(null, "label"); + StringLiteral stringLiteral1 = new StringLiteral("abc"); + BinaryPredicate binaryPredicate1 = new BinaryPredicate(Operator.EQ, slotRef1, stringLiteral1); + + SlotRef slotRef2 = new SlotRef(null, "id"); + IntLiteral intLiteral2 = new IntLiteral(1); + LikePredicate likePredicate = new LikePredicate(LikePredicate.Operator.LIKE, slotRef2, intLiteral2); + + CompoundPredicate compoundPredicate1 = new CompoundPredicate(CompoundPredicate.Operator.AND, binaryPredicate1, likePredicate); + ShowExportStmt stmt1 = new ShowExportStmt(null, compoundPredicate1, null, null); + + ExceptionChecker.expectThrows(AnalysisException.class, () -> stmt1.analyze(analyzer)); + + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/load/loadv2/ExportMgrTest.java b/fe/fe-core/src/test/java/org/apache/doris/load/loadv2/ExportMgrTest.java new file mode 100644 index 0000000..d72318b --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/load/loadv2/ExportMgrTest.java @@ -0,0 +1,99 @@ +// 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.load.loadv2; + +import com.google.common.collect.Maps; +import mockit.Mocked; +import org.apache.doris.analysis.BrokerDesc; +import org.apache.doris.analysis.LoadStmt; +import org.apache.doris.analysis.TableName; +import org.apache.doris.common.jmockit.Deencapsulation; +import org.apache.doris.load.ExportJob; +import org.apache.doris.load.ExportMgr; +import org.apache.doris.mysql.privilege.MockedAuth; +import org.apache.doris.mysql.privilege.PaloAuth; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ExportMgrTest { + private final ExportMgr exportMgr = new ExportMgr(); + + @Mocked + private PaloAuth auth; + + @Before + public void setUp() { + MockedAuth.mockedAuth(auth); + } + + @Test + public void testShowExport() throws Exception { + + ExportJob job1 = makeExportJob(1, "aabbcc"); + ExportJob job2 = makeExportJob(2, "aabbdd"); + ExportJob job3 = makeExportJob(3, "eebbcc"); + + exportMgr.unprotectAddJob(job1); + exportMgr.unprotectAddJob(job2); + exportMgr.unprotectAddJob(job3); + + List<List<String>> r1 = exportMgr.getExportJobInfosByIdOrState(-1, 3, "", true, null, null, -1); + Assert.assertEquals(r1.size(), 1); + + List<List<String>> r2 = exportMgr.getExportJobInfosByIdOrState(-1, 0, "", false, null, null, -1); + Assert.assertEquals(r2.size(), 3); + + List<List<String>> r3 = exportMgr.getExportJobInfosByIdOrState(-1, 0, "aabbcc", false, null, null, -1); + Assert.assertEquals(r3.size(), 1); + + List<List<String>> r4 = exportMgr.getExportJobInfosByIdOrState(-1, 0, "%bb%", true, null, null, -1); + Assert.assertEquals(r4.size(), 3); + + List<List<String>> r5 = exportMgr.getExportJobInfosByIdOrState(-1, 0, "aabb%", true, null, null, -1); + Assert.assertEquals(r5.size(), 2); + + List<List<String>> r6 = exportMgr.getExportJobInfosByIdOrState(-1, 0, "%dd", true, null, null, -1); + Assert.assertEquals(r6.size(), 1); + + } + + private ExportJob makeExportJob(long id, String label) { + ExportJob job1 = new ExportJob(id); + Deencapsulation.setField(job1, "label", label); + + TableName tbl1 = new TableName("testCluster", "testDb"); + Deencapsulation.setField(job1, "tableName", tbl1); + + BrokerDesc bd = new BrokerDesc("broker", new HashMap<>()); + Deencapsulation.setField(job1, "brokerDesc", bd); + + Map<String, String> properties = Maps.newHashMap(); + properties.put(LoadStmt.EXEC_MEM_LIMIT, "-1"); + properties.put(LoadStmt.TIMEOUT_PROPERTY, "-1"); + Deencapsulation.setField(job1, "properties", properties); + + + return job1; + } + +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org