This is an automated email from the ASF dual-hosted git repository.
yiguolei 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 61c817f4cc [feature](syntax) support SELECT * EXCEPT (#13844)
61c817f4cc is described below
commit 61c817f4cc30b2aab0465ac844b9c9323b27abc2
Author: qiye <[email protected]>
AuthorDate: Tue Nov 1 19:41:25 2022 +0800
[feature](syntax) support SELECT * EXCEPT (#13844)
* [feature](syntax) support SELECT * EXCEPT: add regression test
---
docs/en/docs/data-table/basic-usage.md | 18 ++++++
docs/zh-CN/docs/data-table/basic-usage.md | 18 ++++++
fe/fe-core/src/main/cup/sql_parser.cup | 7 +++
.../java/org/apache/doris/analysis/SelectList.java | 12 ++++
.../java/org/apache/doris/analysis/SelectStmt.java | 72 ++++++++++++++--------
.../org/apache/doris/analysis/SelectStmtTest.java | 9 +++
.../suites/correctness/test_select_except.groovy | 69 +++++++++++++++++++++
7 files changed, 181 insertions(+), 24 deletions(-)
diff --git a/docs/en/docs/data-table/basic-usage.md
b/docs/en/docs/data-table/basic-usage.md
index 069fceaa12..3f05aa5e1f 100644
--- a/docs/en/docs/data-table/basic-usage.md
+++ b/docs/en/docs/data-table/basic-usage.md
@@ -376,6 +376,24 @@ MySQL> SELECT * FROM table1 ORDER BY citycode;
5 rows in set (0.01 sec)
```
+### SELECT * EXCEPT
+
+A `SELECT * EXCEPT` statement specifies the names of one or more columns to
exclude from the result. All matching column names are omitted from the output.
+
+```sql
+MySQL> SELECT * except (username, citycode) FROM table1;
++--------+------+
+| siteid | pv |
++--------+------+
+| 2 | 2 |
+| 5 | 3 |
+| 3 | 2 |
++--------+------+
+3 rows in set (0.01 sec)
+```
+
+**Note**: `SELECT * EXCEPT` does not exclude columns that do not have names.
+
### Join Query
Query example::
diff --git a/docs/zh-CN/docs/data-table/basic-usage.md
b/docs/zh-CN/docs/data-table/basic-usage.md
index 4a108e4e3e..37141b9b6c 100644
--- a/docs/zh-CN/docs/data-table/basic-usage.md
+++ b/docs/zh-CN/docs/data-table/basic-usage.md
@@ -401,6 +401,24 @@ mysql> SELECT * FROM table1 ORDER BY citycode;
5 rows in set (0.01 sec)
```
+### SELECT * EXCEPT
+
+`SELECT * EXCEPT` 语句指定要从结果中排除的一个或多个列的名称。输出中将忽略所有匹配的列名称。
+
+```sql
+MySQL> SELECT * except (username, citycode) FROM table1;
++--------+------+
+| siteid | pv |
++--------+------+
+| 2 | 2 |
+| 5 | 3 |
+| 3 | 2 |
++--------+------+
+3 rows in set (0.01 sec)
+```
+
+**注意**:`SELECT * EXCEPT` 不会排除没有名称的列。
+
### Join 查询
查询示例:
diff --git a/fe/fe-core/src/main/cup/sql_parser.cup
b/fe/fe-core/src/main/cup/sql_parser.cup
index 0590f1bc62..c9c6260c60 100644
--- a/fe/fe-core/src/main/cup/sql_parser.cup
+++ b/fe/fe-core/src/main/cup/sql_parser.cup
@@ -891,6 +891,7 @@ precedence left KW_LIKE, KW_REGEXP;
precedence left EQUAL, LESSTHAN, GREATERTHAN;
precedence left ADD, SUBTRACT;
precedence left AT, STAR, DIVIDE, MOD, KW_DIV;
+precedence left KW_EXCEPT;
precedence left BITAND, BITOR, BITXOR;
precedence left KW_PIPE;
precedence left BITNOT;
@@ -4759,6 +4760,12 @@ select_list ::=
list.addItem(SelectListItem.createStarItem(null));
RESULT = list;
:}
+ | STAR KW_EXCEPT LPAREN select_sublist: list RPAREN
+ {:
+ SelectList res = new SelectList(list);
+ list.setIsExcept(true);
+ RESULT = list;
+ :}
;
select_sublist ::=
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectList.java
b/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectList.java
index ee950a032e..e7da518af8 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectList.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectList.java
@@ -36,6 +36,7 @@ public class SelectList {
private static final String SET_VAR_KEY = "set_var";
private boolean isDistinct;
+ private boolean isExcept;
private Map<String, String> optHints;
private List<OrderByElement> orderByElements;
@@ -53,15 +54,18 @@ public class SelectList {
items.add(item.clone());
}
isDistinct = other.isDistinct;
+ isExcept = other.isExcept;
}
public SelectList() {
items = Lists.newArrayList();
this.isDistinct = false;
+ this.isExcept = false;
}
public SelectList(List<SelectListItem> items, boolean isDistinct) {
this.isDistinct = isDistinct;
+ this.isExcept = false;
this.items = items;
}
@@ -81,6 +85,14 @@ public class SelectList {
isDistinct = value;
}
+ public boolean isExcept() {
+ return isExcept;
+ }
+
+ public void setIsExcept(boolean except) {
+ isExcept = except;
+ }
+
public Map<String, String> getOptHints() {
return optHints;
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java
b/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java
index 193ff8a8e8..f522dcd898 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java
@@ -414,33 +414,57 @@ public class SelectStmt extends QueryStmt {
registerIsNotEmptyPredicates(analyzer);
}
// populate selectListExprs, aliasSMap, groupingSmap and colNames
- for (SelectListItem item : selectList.getItems()) {
- if (item.isStar()) {
- TableName tblName = item.getTblName();
- if (tblName == null) {
- expandStar(analyzer);
- } else {
- expandStar(analyzer, tblName);
- }
+ if (selectList.isExcept()) {
+ List<SelectListItem> items = selectList.getItems();
+ TableName tblName = items.get(0).getTblName();
+ if (tblName == null) {
+ expandStar(analyzer);
} else {
- // Analyze the resultExpr before generating a label to ensure
enforcement
- // of expr child and depth limits (toColumn() label may call
toSql()).
- item.getExpr().analyze(analyzer);
- if (!(item.getExpr() instanceof CaseExpr)
- &&
item.getExpr().contains(Predicates.instanceOf(Subquery.class))) {
- throw new AnalysisException("Subquery is not supported in
the select list.");
+ expandStar(analyzer, tblName);
+ }
+
+ // get excepted cols
+ ArrayList<String> exceptCols = new ArrayList<>();
+ for (SelectListItem item : items) {
+ Expr expr = item.getExpr();
+ if (!(item.getExpr() instanceof SlotRef)) {
+ throw new AnalysisException("`SELECT * EXCEPT` only
supports column name.");
}
- Expr expr = rewriteQueryExprByMvColumnExpr(item.getExpr(),
analyzer);
- resultExprs.add(expr);
- SlotRef aliasRef = new SlotRef(null, item.toColumnLabel());
- Expr existingAliasExpr = aliasSMap.get(aliasRef);
- if (existingAliasExpr != null &&
!existingAliasExpr.equals(item.getExpr())) {
- // If we have already seen this alias, it refers to more
than one column and
- // therefore is ambiguous.
- ambiguousAliasList.add(aliasRef);
+ exceptCols.add(expr.toColumnLabel());
+ }
+ // remove excepted columns
+ resultExprs.removeIf(expr ->
exceptCols.contains(expr.toColumnLabel()));
+ colLabels.removeIf(exceptCols::contains);
+
+ } else {
+ for (SelectListItem item : selectList.getItems()) {
+ if (item.isStar()) {
+ TableName tblName = item.getTblName();
+ if (tblName == null) {
+ expandStar(analyzer);
+ } else {
+ expandStar(analyzer, tblName);
+ }
+ } else {
+ // Analyze the resultExpr before generating a label to
ensure enforcement
+ // of expr child and depth limits (toColumn() label may
call toSql()).
+ item.getExpr().analyze(analyzer);
+ if (!(item.getExpr() instanceof CaseExpr)
+ &&
item.getExpr().contains(Predicates.instanceOf(Subquery.class))) {
+ throw new AnalysisException("Subquery is not supported
in the select list.");
+ }
+ Expr expr = rewriteQueryExprByMvColumnExpr(item.getExpr(),
analyzer);
+ resultExprs.add(expr);
+ SlotRef aliasRef = new SlotRef(null, item.toColumnLabel());
+ Expr existingAliasExpr = aliasSMap.get(aliasRef);
+ if (existingAliasExpr != null &&
!existingAliasExpr.equals(item.getExpr())) {
+ // If we have already seen this alias, it refers to
more than one column and
+ // therefore is ambiguous.
+ ambiguousAliasList.add(aliasRef);
+ }
+ aliasSMap.put(aliasRef, item.getExpr().clone());
+ colLabels.add(item.toColumnLabel());
}
- aliasSMap.put(aliasRef, item.getExpr().clone());
- colLabels.add(item.toColumnLabel());
}
}
if (groupByClause != null && groupByClause.isGroupByExtension()) {
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/analysis/SelectStmtTest.java
b/fe/fe-core/src/test/java/org/apache/doris/analysis/SelectStmtTest.java
index ecb81927ab..dc66b2ea6a 100755
--- a/fe/fe-core/src/test/java/org/apache/doris/analysis/SelectStmtTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/SelectStmtTest.java
@@ -937,4 +937,13 @@ public class SelectStmtTest {
Set<Long> sampleTabletIds16 = ((OlapScanNode)
planner16.getScanNodes().get(0)).getSampleTabletIds();
Assert.assertEquals(1, sampleTabletIds16.size());
}
+
+ @Test
+ public void testSelectExcept() throws Exception {
+ ConnectContext ctx = UtFrameUtils.createDefaultCtx();
+ String sql = "SELECT * EXCEPT (siteid) FROM db1.table1";
+ SelectStmt stmt = (SelectStmt) UtFrameUtils.parseAndAnalyzeStmt(sql,
ctx);
+ Assert.assertFalse(stmt.getColLabels().contains("siteid"));
+ Assert.assertEquals(stmt.resultExprs.size(), 3);
+ }
}
diff --git a/regression-test/suites/correctness/test_select_except.groovy
b/regression-test/suites/correctness/test_select_except.groovy
new file mode 100644
index 0000000000..ddcb0ade6d
--- /dev/null
+++ b/regression-test/suites/correctness/test_select_except.groovy
@@ -0,0 +1,69 @@
+// 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.
+
+suite("test_select_except") {
+ sql """ DROP TABLE IF EXISTS tbl_select_except """
+ sql """
+ CREATE TABLE tbl_select_except (
+ siteid INT DEFAULT '10',
+ citycode SMALLINT,
+ username VARCHAR(32) DEFAULT '',
+ pv BIGINT SUM DEFAULT '0'
+ ) ENGINE=OLAP
+ AGGREGATE KEY(siteid, citycode, username)
+ DISTRIBUTED BY HASH(siteid) BUCKETS 10
+ PROPERTIES (
+ "replication_allocation" = "tag.location.default: 1",
+ "in_memory" = "false",
+ "storage_format" = "V2"
+ );
+ """
+ sql """
+ insert into tbl_select_except values(1,1,'jim',2)
+ """
+ sql """
+ insert into tbl_select_except values(2,1,'grace',3)
+ """
+ sql """
+ insert into tbl_select_except values(3,2,'tom',4)
+ """
+
+ List<List<Object>> results = sql "select * except (siteid, citycode) from
tbl_select_except order by username"
+
+ assertEquals(results.size(), 3)
+ assertEquals(results[0].size(), 2)
+ assertEquals(results[1].size(), 2)
+ assertEquals(results[2].size(), 2)
+
+ assertEquals(results[0][0], 'grace')
+ assertEquals(results[1][0], 'jim')
+ assertEquals(results[2][0], 'tom')
+ assertEquals(results[0][1], 3)
+ assertEquals(results[1][1], 2)
+ assertEquals(results[2][1], 4)
+
+ try {
+ test {
+ sql """
+ select * except (concat(username, 's')) from tbl_select_except
order by username
+ """
+ exception "errCode = 2, detailMessage = `SELECT * EXCEPT` only
supports column name."
+ }
+ } finally {
+ sql "drop table if exists tbl_select_except"
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]