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
"
"Other Grammar","Select Expression","
* | expression [ [ AS ] columnAlias ] | tableAlias.*
wildcardExpression | expression [ [ AS ] columnAlias ]
","
An expression in a SELECT statement.
","
......@@ -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;
"
"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","
windowName | windowSpecification
","
......
......@@ -2593,7 +2593,7 @@ public class Parser {
ArrayList<Expression> expressions = Utils.newSmallArrayList();
do {
if (readIf(ASTERISK)) {
expressions.add(new Wildcard(null, null));
expressions.add(parseWildcard(null, null));
} else {
Expression expr = readExpression();
if (readIf("AS") || currentTokenType == IDENTIFIER) {
......@@ -3506,7 +3506,7 @@ public class Parser {
private Expression readWildcardOrSequenceValue(String schema,
String objectName) {
if (readIf(ASTERISK)) {
return new Wildcard(schema, objectName);
return parseWildcard(schema, objectName);
}
if (schema == null) {
schema = session.getCurrentSchemaName();
......@@ -3531,6 +3531,38 @@ public class Parser {
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) {
Expression expr = readWildcardOrSequenceValue(null, objectName);
if (expr != null) {
......
......@@ -892,40 +892,53 @@ public class Select extends Query {
}
String schemaName = expr.getSchemaName();
String tableAlias = expr.getTableAlias();
Wildcard w = (Wildcard) expr;
boolean hasExceptColumns = w.getExceptColumns() != null;
HashMap<Column, ExpressionColumn> exceptTableColumns = null;
if (tableAlias == null) {
if (hasExceptColumns) {
for (TableFilter filter : filters) {
w.mapColumns(filter, 1, Expression.MAP_INITIAL);
}
exceptTableColumns = w.mapExceptColumns();
}
expressions.remove(i);
for (TableFilter filter : filters) {
i = expandColumnList(filter, i);
i = expandColumnList(filter, i, exceptTableColumns);
}
i--;
} else {
TableFilter filter = null;
for (TableFilter f : filters) {
if (db.equalsIdentifiers(tableAlias, f.getTableAlias())) {
if (schemaName == null ||
db.equalsIdentifiers(schemaName,
f.getSchemaName())) {
if (schemaName == null || db.equalsIdentifiers(schemaName, f.getSchemaName())) {
if (hasExceptColumns) {
w.mapColumns(f, 1, Expression.MAP_INITIAL);
exceptTableColumns = w.mapExceptColumns();
}
filter = f;
break;
}
}
}
if (filter == null) {
throw DbException.get(ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1,
tableAlias);
throw DbException.get(ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1, tableAlias);
}
expressions.remove(i);
i = expandColumnList(filter, i);
i = expandColumnList(filter, i, exceptTableColumns);
i--;
}
}
}
private int expandColumnList(TableFilter filter, int index) {
private int expandColumnList(TableFilter filter, int index, HashMap<Column, ExpressionColumn> except) {
Table t = filter.getTable();
String alias = filter.getTableAlias();
Column[] columns = t.getColumns();
for (Column c : columns) {
if (except != null && except.remove(c) != null) {
continue;
}
if (!c.getVisible()) {
continue;
}
......
......@@ -140,18 +140,22 @@ public class ExpressionColumn extends Expression {
return constant.getValue();
}
}
String name = columnName;
if (tableAlias != null) {
name = tableAlias + "." + name;
if (schemaName != null) {
name = schemaName + "." + name;
}
}
throw DbException.get(ErrorCode.COLUMN_NOT_FOUND_1, name);
throw getColumnException(ErrorCode.COLUMN_NOT_FOUND_1);
}
return columnResolver.optimize(this, column);
}
public DbException getColumnException(int code) {
String name = columnName;
if (tableAlias != null) {
name = tableAlias + '.' + name;
if (schemaName != null) {
name = schemaName + '.' + name;
}
}
return DbException.get(code, name);
}
@Override
public void updateAggregate(Session session, int stage) {
Select select = columnResolver.getSelect();
......
......@@ -5,9 +5,13 @@
*/
package org.h2.expression;
import java.util.ArrayList;
import java.util.HashMap;
import org.h2.api.ErrorCode;
import org.h2.engine.Session;
import org.h2.message.DbException;
import org.h2.table.Column;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.util.StringUtils;
......@@ -22,11 +26,41 @@ public class Wildcard extends Expression {
private final String schema;
private final String table;
private ArrayList<ExpressionColumn> exceptColumns;
public Wildcard(String schema, String table) {
this.schema = schema;
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
public boolean isWildcard() {
return true;
......@@ -44,7 +78,11 @@ public class Wildcard extends Expression {
@Override
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
......@@ -84,10 +122,23 @@ public class Wildcard extends Expression {
@Override
public String getSQL() {
if (table == null) {
return "*";
StringBuilder builder = new StringBuilder();
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
......
......@@ -329,3 +329,81 @@ SELECT * FROM TEST ORDER BY ?, ? FETCH FIRST ROW ONLY;
DROP TABLE TEST;
> 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
partitioned tri partitions
discard enhancements nolock surefire logarithm
qualification opportunity jumping exploited unacceptable vrs
qualification opportunity jumping exploited unacceptable vrs duplicated
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论