Unverified 提交 31ed7544 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1510 from katzyn/except

Add optional EXCEPT clause to wildcards
...@@ -2497,7 +2497,7 @@ COMPRESSION LZF ...@@ -2497,7 +2497,7 @@ COMPRESSION LZF
" "
"Other Grammar","Select Expression"," "Other Grammar","Select Expression","
* | expression [ [ AS ] columnAlias ] | tableAlias.* wildcardExpression | expression [ [ AS ] columnAlias ]
"," ","
An expression in a SELECT statement. An expression in a SELECT statement.
"," ","
...@@ -2568,6 +2568,16 @@ The column list of the resulting table is C1, C2, and so on. ...@@ -2568,6 +2568,16 @@ The column list of the resulting table is C1, C2, and so on.
SELECT * FROM (VALUES(1, 'Hello'), (2, 'World')) AS V; SELECT * FROM (VALUES(1, 'Hello'), (2, 'World')) AS V;
" "
"Other Grammar","Wildcard expression","
{* | tableAlias.*} [EXCEPT ([tableAlias.]columnName, [,...])]
","
A wildcard expression in a SELECT statement.
A wildcard expression represents all visible columns. Some columns can be excluded with optional EXCEPT clause.
","
*
* EXCEPT (DATA)
"
"Other Grammar","Window name or specification"," "Other Grammar","Window name or specification","
windowName | windowSpecification windowName | windowSpecification
"," ","
......
...@@ -2593,7 +2593,7 @@ public class Parser { ...@@ -2593,7 +2593,7 @@ public class Parser {
ArrayList<Expression> expressions = Utils.newSmallArrayList(); ArrayList<Expression> expressions = Utils.newSmallArrayList();
do { do {
if (readIf(ASTERISK)) { if (readIf(ASTERISK)) {
expressions.add(new Wildcard(null, null)); expressions.add(parseWildcard(null, null));
} else { } else {
Expression expr = readExpression(); Expression expr = readExpression();
if (readIf("AS") || currentTokenType == IDENTIFIER) { if (readIf("AS") || currentTokenType == IDENTIFIER) {
...@@ -3506,7 +3506,7 @@ public class Parser { ...@@ -3506,7 +3506,7 @@ public class Parser {
private Expression readWildcardOrSequenceValue(String schema, private Expression readWildcardOrSequenceValue(String schema,
String objectName) { String objectName) {
if (readIf(ASTERISK)) { if (readIf(ASTERISK)) {
return new Wildcard(schema, objectName); return parseWildcard(schema, objectName);
} }
if (schema == null) { if (schema == null) {
schema = session.getCurrentSchemaName(); schema = session.getCurrentSchemaName();
...@@ -3531,6 +3531,38 @@ public class Parser { ...@@ -3531,6 +3531,38 @@ public class Parser {
return null; return null;
} }
private Wildcard parseWildcard(String schema, String objectName) {
Wildcard wildcard = new Wildcard(schema, objectName);
if (readIf(EXCEPT)) {
read(OPEN_PAREN);
ArrayList<ExpressionColumn> exceptColumns = Utils.newSmallArrayList();
do {
String s = null, t = null;
String name = readColumnIdentifier();
if (readIf(DOT)) {
t = name;
name = readColumnIdentifier();
if (readIf(DOT)) {
s = t;
t = name;
name = readColumnIdentifier();
if (readIf(DOT)) {
if (!equalsToken(database.getShortName(), s)) {
throw DbException.get(ErrorCode.DATABASE_NOT_FOUND_1, s);
}
s = t;
t = name;
name = readColumnIdentifier();
}
}
}
exceptColumns.add(new ExpressionColumn(database, s, t, name));
} while (readIfMore(true));
wildcard.setExceptColumns(exceptColumns);
}
return wildcard;
}
private Expression readTermObjectDot(String objectName) { private Expression readTermObjectDot(String objectName) {
Expression expr = readWildcardOrSequenceValue(null, objectName); Expression expr = readWildcardOrSequenceValue(null, objectName);
if (expr != null) { if (expr != null) {
......
...@@ -892,40 +892,53 @@ public class Select extends Query { ...@@ -892,40 +892,53 @@ public class Select extends Query {
} }
String schemaName = expr.getSchemaName(); String schemaName = expr.getSchemaName();
String tableAlias = expr.getTableAlias(); String tableAlias = expr.getTableAlias();
Wildcard w = (Wildcard) expr;
boolean hasExceptColumns = w.getExceptColumns() != null;
HashMap<Column, ExpressionColumn> exceptTableColumns = null;
if (tableAlias == null) { if (tableAlias == null) {
if (hasExceptColumns) {
for (TableFilter filter : filters) {
w.mapColumns(filter, 1, Expression.MAP_INITIAL);
}
exceptTableColumns = w.mapExceptColumns();
}
expressions.remove(i); expressions.remove(i);
for (TableFilter filter : filters) { for (TableFilter filter : filters) {
i = expandColumnList(filter, i); i = expandColumnList(filter, i, exceptTableColumns);
} }
i--; i--;
} else { } else {
TableFilter filter = null; TableFilter filter = null;
for (TableFilter f : filters) { for (TableFilter f : filters) {
if (db.equalsIdentifiers(tableAlias, f.getTableAlias())) { if (db.equalsIdentifiers(tableAlias, f.getTableAlias())) {
if (schemaName == null || if (schemaName == null || db.equalsIdentifiers(schemaName, f.getSchemaName())) {
db.equalsIdentifiers(schemaName, if (hasExceptColumns) {
f.getSchemaName())) { w.mapColumns(f, 1, Expression.MAP_INITIAL);
exceptTableColumns = w.mapExceptColumns();
}
filter = f; filter = f;
break; break;
} }
} }
} }
if (filter == null) { if (filter == null) {
throw DbException.get(ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1, throw DbException.get(ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1, tableAlias);
tableAlias);
} }
expressions.remove(i); expressions.remove(i);
i = expandColumnList(filter, i); i = expandColumnList(filter, i, exceptTableColumns);
i--; i--;
} }
} }
} }
private int expandColumnList(TableFilter filter, int index) { private int expandColumnList(TableFilter filter, int index, HashMap<Column, ExpressionColumn> except) {
Table t = filter.getTable(); Table t = filter.getTable();
String alias = filter.getTableAlias(); String alias = filter.getTableAlias();
Column[] columns = t.getColumns(); Column[] columns = t.getColumns();
for (Column c : columns) { for (Column c : columns) {
if (except != null && except.remove(c) != null) {
continue;
}
if (!c.getVisible()) { if (!c.getVisible()) {
continue; continue;
} }
......
...@@ -140,16 +140,20 @@ public class ExpressionColumn extends Expression { ...@@ -140,16 +140,20 @@ public class ExpressionColumn extends Expression {
return constant.getValue(); return constant.getValue();
} }
} }
throw getColumnException(ErrorCode.COLUMN_NOT_FOUND_1);
}
return columnResolver.optimize(this, column);
}
public DbException getColumnException(int code) {
String name = columnName; String name = columnName;
if (tableAlias != null) { if (tableAlias != null) {
name = tableAlias + "." + name; name = tableAlias + '.' + name;
if (schemaName != null) { if (schemaName != null) {
name = schemaName + "." + name; name = schemaName + '.' + name;
}
} }
throw DbException.get(ErrorCode.COLUMN_NOT_FOUND_1, name);
} }
return columnResolver.optimize(this, column); return DbException.get(code, name);
} }
@Override @Override
......
...@@ -5,9 +5,13 @@ ...@@ -5,9 +5,13 @@
*/ */
package org.h2.expression; package org.h2.expression;
import java.util.ArrayList;
import java.util.HashMap;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.table.Column;
import org.h2.table.ColumnResolver; import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -22,11 +26,41 @@ public class Wildcard extends Expression { ...@@ -22,11 +26,41 @@ public class Wildcard extends Expression {
private final String schema; private final String schema;
private final String table; private final String table;
private ArrayList<ExpressionColumn> exceptColumns;
public Wildcard(String schema, String table) { public Wildcard(String schema, String table) {
this.schema = schema; this.schema = schema;
this.table = table; this.table = table;
} }
public ArrayList<ExpressionColumn> getExceptColumns() {
return exceptColumns;
}
public void setExceptColumns(ArrayList<ExpressionColumn> exceptColumns) {
this.exceptColumns = exceptColumns;
}
/**
* Returns map of excluded table columns to expression columns and validates
* that all columns are resolved and not duplicated.
*
* @return map of excluded table columns to expression columns
*/
public HashMap<Column, ExpressionColumn> mapExceptColumns() {
HashMap<Column, ExpressionColumn> exceptTableColumns = new HashMap<>();
for (ExpressionColumn ec : exceptColumns) {
Column column = ec.getColumn();
if (column == null) {
throw ec.getColumnException(ErrorCode.COLUMN_NOT_FOUND_1);
}
if (exceptTableColumns.put(column, ec) != null) {
throw ec.getColumnException(ErrorCode.DUPLICATE_COLUMN_NAME_1);
}
}
return exceptTableColumns;
}
@Override @Override
public boolean isWildcard() { public boolean isWildcard() {
return true; return true;
...@@ -44,7 +78,11 @@ public class Wildcard extends Expression { ...@@ -44,7 +78,11 @@ public class Wildcard extends Expression {
@Override @Override
public void mapColumns(ColumnResolver resolver, int level, int state) { public void mapColumns(ColumnResolver resolver, int level, int state) {
throw DbException.get(ErrorCode.SYNTAX_ERROR_1, table); if (exceptColumns != null) {
for (ExpressionColumn column : exceptColumns) {
column.mapColumns(resolver, level, state);
}
}
} }
@Override @Override
...@@ -84,10 +122,23 @@ public class Wildcard extends Expression { ...@@ -84,10 +122,23 @@ public class Wildcard extends Expression {
@Override @Override
public String getSQL() { public String getSQL() {
if (table == null) { StringBuilder builder = new StringBuilder();
return "*"; if (table != null) {
builder.append(StringUtils.quoteIdentifier(table)).append('.');
}
builder.append('*');
if (exceptColumns != null) {
builder.append(" EXCEPT (");
for (int i = 0; i < exceptColumns.size(); i++) {
if (i > 0) {
builder.append(", ");
}
ExpressionColumn ec = exceptColumns.get(i);
builder.append(ec.getSQL());
}
builder.append(')');
} }
return StringUtils.quoteIdentifier(table) + ".*"; return builder.toString();
} }
@Override @Override
......
...@@ -329,3 +329,81 @@ SELECT * FROM TEST ORDER BY ?, ? FETCH FIRST ROW ONLY; ...@@ -329,3 +329,81 @@ SELECT * FROM TEST ORDER BY ?, ? FETCH FIRST ROW ONLY;
DROP TABLE TEST; DROP TABLE TEST;
> ok > ok
CREATE TABLE TEST1(A INT, B INT, C INT) AS SELECT 1, 2, 3;
> ok
CREATE TABLE TEST2(A INT, D INT) AS SELECT 4, 5;
> ok
SELECT * FROM TEST1, TEST2;
> A B C A D
> - - - - -
> 1 2 3 4 5
> rows: 1
SELECT * EXCEPT (A) FROM TEST1;
> B C
> - -
> 2 3
> rows: 1
SELECT * EXCEPT (TEST1.A) FROM TEST1;
> B C
> - -
> 2 3
> rows: 1
SELECT * EXCEPT (PUBLIC.TEST1.A) FROM TEST1;
> B C
> - -
> 2 3
> rows: 1
SELECT * EXCEPT (SCRIPT.PUBLIC.TEST1.A) FROM TEST1;
> B C
> - -
> 2 3
> rows: 1
SELECT * EXCEPT (Z) FROM TEST1;
> exception COLUMN_NOT_FOUND_1
SELECT * EXCEPT (B, TEST1.B) FROM TEST1;
> exception DUPLICATE_COLUMN_NAME_1
SELECT * EXCEPT (A) FROM TEST1, TEST2;
> exception AMBIGUOUS_COLUMN_NAME_1
SELECT * EXCEPT (TEST1.A, B, TEST2.D) FROM TEST1, TEST2;
> C A
> - -
> 3 4
> rows: 1
SELECT TEST1.*, TEST2.* FROM TEST1, TEST2;
> A B C A D
> - - - - -
> 1 2 3 4 5
> rows: 1
SELECT TEST1.* EXCEPT (A), TEST2.* EXCEPT (A) FROM TEST1, TEST2;
> B C D
> - - -
> 2 3 5
> rows: 1
SELECT TEST1.* EXCEPT (A), TEST2.* EXCEPT (D) FROM TEST1, TEST2;
> B C A
> - - -
> 2 3 4
> rows: 1
SELECT * EXCEPT (T1.A, T2.D) FROM TEST1 T1, TEST2 T2;
> B C A
> - - -
> 2 3 4
> rows: 1
DROP TABLE TEST1, TEST2;
> ok
...@@ -800,4 +800,4 @@ ranks rno dro rko precede cume reopens preceding unbounded rightly itr lag maxim ...@@ -800,4 +800,4 @@ ranks rno dro rko precede cume reopens preceding unbounded rightly itr lag maxim
partitioned tri partitions partitioned tri partitions
discard enhancements nolock surefire logarithm discard enhancements nolock surefire logarithm
qualification opportunity jumping exploited unacceptable vrs qualification opportunity jumping exploited unacceptable vrs duplicated
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论