Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ catch (RecognitionException e) {
//-----------------------------------------------------------------------------------

tableAllColumns
: STAR
-> ^(TOK_ALLCOLREF)
| tableName DOT STAR
-> ^(TOK_ALLCOLREF tableName)
: STAR (KW_EXCLUDE LPAREN columnNameList RPAREN)?
-> ^(TOK_ALLCOLREF columnNameList?)
| tableName DOT STAR (KW_EXCLUDE LPAREN columnNameList RPAREN)?
-> ^(TOK_ALLCOLREF tableName columnNameList?)
;

// (table|column)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ KW_SYSTEM_TIME: 'SYSTEM_TIME';
KW_SYSTEM_VERSION: 'SYSTEM_VERSION';
KW_EXPIRE_SNAPSHOTS: 'EXPIRE_SNAPSHOTS';
KW_REWRITE_MANIFESTS: 'REWRITE_MANIFESTS';
KW_EXCLUDE: 'EXCLUDE';
KW_SET_CURRENT_SNAPSHOT: 'SET_CURRENT_SNAPSHOT';
KW_BRANCH: 'BRANCH';
KW_SNAPSHOTS: 'SNAPSHOTS';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,7 @@ nonReserved
| KW_TRIM
| KW_SPEC
| KW_SYSTEM_TIME | KW_SYSTEM_VERSION
| KW_EXCLUDE
| KW_EXPIRE_SNAPSHOTS
| KW_REWRITE_MANIFESTS
| KW_SET_CURRENT_SNAPSHOT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4366,8 +4366,11 @@ private Pair<RelNode, RowResolver> internalGenSelectLogicalPlan(QB qb, RelNode s

// 6.4 Build ExprNode corresponding to colums
if (expr.getType() == HiveParser.TOK_ALLCOLREF) {
pos = genRexNodeRegex(".*",
expr.getChildCount() == 0 ? null : getUnescapedName((ASTNode) expr.getChild(0)).toLowerCase(),
// Parse SELECT * EXCLUDE columns and pass them to the Calcite engine for exclusion
ExcludeResult excludeResult = processAllColRefAndExclude(expr, inputRR);
String starTabAlias = excludeResult.tableAlias();
excludedColumns.addAll(excludeResult.excludedColumns());
pos = genRexNodeRegex(".*", starTabAlias,
expr, columnList, excludedColumns, inputRR, starRR, pos, outputRR, qb.getAliases(), true);
} else if (expr.getType() == HiveParser.TOK_TABLE_OR_COL
&& !hasAsClause
Expand Down
47 changes: 44 additions & 3 deletions ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -4737,6 +4737,40 @@ static boolean isRegex(String pattern, HiveConf conf) {
return false;
}

protected record ExcludeResult(String tableAlias, Set<ColumnInfo> excludedColumns) {}

protected ExcludeResult processAllColRefAndExclude(ASTNode expr, RowResolver inputRR)
throws SemanticException {
// Check if the query uses SELECT * EXCLUDE. If it does, grab the table
// alias (like t.*) and build a list of the columns the user wants to exclude.
String starTabAlias = null;
ASTNode excludeNode = null;
Set<ColumnInfo> excludedColumns = new HashSet<>();

if (expr.getChildren() != null) {
for (Node childNode : expr.getChildren()) {
ASTNode child = (ASTNode) childNode;
switch (child.getType()) {
case HiveParser.TOK_TABNAME -> starTabAlias = getUnescapedName(child).toLowerCase();
case HiveParser.TOK_TABCOLNAME -> excludeNode = child;
default ->
throw new SemanticException(
"Unexpected node type in TOK_ALLCOLREF: " + child.getType());
}
}
}

if (excludeNode != null) {
for (int e = 0; e < excludeNode.getChildCount(); e++) {
String excludeColName = unescapeIdentifier(excludeNode.getChild(e).getText()).toLowerCase();
ColumnInfo colInfo = inputRR.get(starTabAlias, excludeColName);
if (colInfo != null) {
excludedColumns.add(colInfo);
}
}
}
return new ExcludeResult(starTabAlias, excludedColumns);
}

private Operator<?> genSelectPlan(String dest, QB qb, Operator<?> input,
Operator<?> inputForSelectStar) throws SemanticException {
Expand Down Expand Up @@ -4914,9 +4948,16 @@ private Operator<?> genSelectPlan(String dest, ASTNode selExprList, QB qb, Opera
// The real expression
if (expr.getType() == HiveParser.TOK_ALLCOLREF) {
int initPos = pos;
pos = genExprNodeDescRegex(".*", expr.getChildCount() == 0 ? null
: getUnescapedName((ASTNode) expr.getChild(0)).toLowerCase(),
expr, colList, null, inputRR, starRR, pos, out_rwsch, qb.getAliases(), false);

ExcludeResult excludeResult = processAllColRefAndExclude(expr, inputRR);
String starTabAlias = excludeResult.tableAlias();
Set<ColumnInfo> excludeCols = excludeResult.excludedColumns();
if (excludeCols.isEmpty()) {
excludeCols = null;
}

pos = genExprNodeDescRegex(".*", starTabAlias,
expr, colList, excludeCols, inputRR, starRR, pos, out_rwsch, qb.getAliases(), false);
if (unparseTranslator.isEnabled()) {
offset += pos - initPos - 1;
}
Expand Down
39 changes: 39 additions & 0 deletions ql/src/test/queries/clientpositive/select_exclude.q
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
set hive.cli.print.header=true;

CREATE TABLE test_exclude (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add

set hive.cli.print.header=true;

to the beginning of the file. IMHO, it would be beneficial to see which column names appear in the result sets.

id INT,
name STRING,
email STRING,
address STRING,
phone STRING
);

INSERT INTO test_exclude VALUES
(1, 'Alice', 'alice@test.com', '123 Apple St', '555-0100'),
(2, 'Bob', 'bob@test.com', '456 Banana Ave', '555-0200');

-- Exclude a single column
EXPLAIN SELECT * EXCLUDE (email) FROM test_exclude;
SELECT * EXCLUDE (email) FROM test_exclude;

-- Exclude multiple columns
EXPLAIN SELECT * EXCLUDE (email, address, phone) FROM test_exclude;
SELECT * EXCLUDE (email, address, phone) FROM test_exclude;

-- Exclude with table alias
EXPLAIN SELECT t.* EXCLUDE (id, phone) FROM test_exclude t;
SELECT t.* EXCLUDE (id, phone) FROM test_exclude t;

-- Exclude with JOIN
CREATE TABLE test_exclude_join (
id INT,
department STRING
);
INSERT INTO test_exclude_join VALUES (1, 'Engineering'), (2, 'Sales');

EXPLAIN
SELECT a.* EXCLUDE (address, phone), b.* EXCLUDE (id)
FROM test_exclude a JOIN test_exclude_join b ON a.id = b.id;

SELECT a.* EXCLUDE (address, phone), b.* EXCLUDE (id)
FROM test_exclude a JOIN test_exclude_join b ON a.id = b.id;
Loading
Loading