Skip to content

Commit

Permalink
[CALCITE-1581] Support UDTF like Hive
Browse files Browse the repository at this point in the history
Close #1138
  • Loading branch information
pengzhiwei authored and chunweilei committed Jul 31, 2020
1 parent 2088488 commit 62ee42f
Show file tree
Hide file tree
Showing 20 changed files with 596 additions and 20 deletions.
42 changes: 33 additions & 9 deletions core/src/main/codegen/templates/Parser.jj
Original file line number Diff line number Diff line change
Expand Up @@ -1822,23 +1822,47 @@ List<SqlNode> SelectList() :
SqlNode SelectItem() :
{
SqlNode e;
SqlIdentifier columnAlias;
final List<SqlNode> columnAliases = new ArrayList();
final Span s = span();
final SqlIdentifier id;
}
{
e = SelectExpression()
[
[ <AS> ]
(
id = SimpleIdentifier()
LOOKAHEAD(2)
(
[ <AS> ]
(
id = SimpleIdentifier()
|
// Mute the warning about ambiguity between alias and continued
// string literal.
LOOKAHEAD(1)
id = SimpleIdentifierFromStringLiteral()
)
{
e = SqlStdOperatorTable.AS.createCall(span().end(e), e, id);
}
)
|
// Mute the warning about ambiguity between alias and continued
// string literal.
LOOKAHEAD(1)
id = SimpleIdentifierFromStringLiteral()
(
<AS> <LPAREN>
columnAlias = SimpleIdentifier() {
columnAliases.add(columnAlias);
}
( <COMMA> columnAlias = SimpleIdentifier() { columnAliases.add(columnAlias);} )*
<RPAREN> {
if (!this.conformance.allowSelectTableFunction()) {
throw SqlUtil.newContextException(getPos(),
RESOURCE.notAllowTableFunctionInSelect());
}
e = SqlStdOperatorTable.AS.createCall(s.end(e), e,
new SqlNodeList(columnAliases, s.end(e)));
}
)
)
{
e = SqlStdOperatorTable.AS.createCall(span().end(e), e, id);
}
]
{
return e;
Expand Down
12 changes: 12 additions & 0 deletions core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,18 @@ ExInst<CalciteException> invalidTypesForComparison(String clazzName0, String op,
@BaseMessage("Not a valid input for REGEXP_REPLACE: ''{0}''")
ExInst<CalciteException> invalidInputForRegexpReplace(String value);

@BaseMessage("Table function is not allowed in select list in current SQL conformance level")
ExInst<SqlValidatorException> notAllowTableFunctionInSelect();

@BaseMessage("''{0}'' should be a table function")
ExInst<SqlValidatorException> exceptTableFunction(String name);

@BaseMessage("Only one table function is allowed in select list")
ExInst<SqlValidatorException> onlyOneTableFunctionAllowedInSelect();

@BaseMessage("Table function is not allowed in aggregate statement")
ExInst<SqlValidatorException> notAllowTableFunctionInAggregate();

@BaseMessage("Illegal xslt specified : ''{0}''")
ExInst<CalciteException> illegalXslt(String xslt);

Expand Down
25 changes: 20 additions & 5 deletions core/src/main/java/org/apache/calcite/sql/SqlAsOperator.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.apache.calcite.sql.validate.SqlValidatorScope;
import org.apache.calcite.util.Util;

import java.util.ArrayList;
import java.util.List;

import static org.apache.calcite.util.Static.RESOURCE;
Expand Down Expand Up @@ -102,12 +103,26 @@ public void validateCall(
// we don't want to validate the identifier.
final List<SqlNode> operands = call.getOperandList();
assert operands.size() == 2;
assert operands.get(1) instanceof SqlIdentifier;
operands.get(0).validateExpr(validator, scope);
SqlIdentifier id = (SqlIdentifier) operands.get(1);
if (!id.isSimple()) {
throw validator.newValidationError(id,
RESOURCE.aliasMustBeSimpleIdentifier());

SqlNode asIdentifier = operands.get(1);
assert asIdentifier instanceof SqlIdentifier
|| (asIdentifier instanceof SqlNodeList
&& validator.config().sqlConformance().allowSelectTableFunction());

List<SqlNode> ids = new ArrayList<>();
if (asIdentifier instanceof SqlIdentifier) {
ids.add(operands.get(1));
} else {
ids.addAll(((SqlNodeList) operands.get(1)).getList());
}
for (int i = 0; i < ids.size(); i++) {
assert ids.get(i) instanceof SqlIdentifier;
SqlIdentifier id = (SqlIdentifier) ids.get(i);
if (!id.isSimple()) {
throw validator.newValidationError(id,
RESOURCE.aliasMustBeSimpleIdentifier());
}
}
}

Expand Down
15 changes: 15 additions & 0 deletions core/src/main/java/org/apache/calcite/sql/SqlUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -1189,4 +1189,19 @@ private void visitChild(SqlNode node) {
return check(type);
}
}

/**
* Whether the selectItem is a table function node in the select.
* eg. "select table_func(1) as (f0,f1)"
*
* @param selectItem select item
* @return true if this selectItem is a table function call
*/
public static boolean isTableFunctionInSelect(SqlNode selectItem) {
if (selectItem.getKind() == SqlKind.AS) {
SqlBasicCall call = (SqlBasicCall) selectItem;
return call.getOperands()[1] instanceof SqlNodeList;
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlNodeList;
import org.apache.calcite.sql.SqlUtil;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.util.Util;
Expand Down Expand Up @@ -70,7 +71,12 @@ protected RelDataType validateImpl(RelDataType targetRowType) {
final SqlValidatorNamespace childNs =
validator.getNamespace(operands.get(0));
final RelDataType rowType = childNs.getRowTypeSansSystemColumns();
final List<SqlNode> columnNames = Util.skip(operands, 2);
List<SqlNode> columnNames;
if (SqlUtil.isTableFunctionInSelect(call)) {
columnNames = ((SqlNodeList) operands.get(1)).getList();
} else {
columnNames = Util.skip(operands, 2);
}
for (final SqlNode operand : columnNames) {
String name = ((SqlIdentifier) operand).getSimple();
if (nameList.contains(name)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ public boolean allowPluralTimeUnits() {
return SqlConformanceEnum.DEFAULT.allowPluralTimeUnits();
}

public boolean allowSelectTableFunction() {
return SqlConformanceEnum.DEFAULT.allowSelectTableFunction();
}

public boolean allowQualifyingCommonColumn() {
return SqlConformanceEnum.DEFAULT.allowQualifyingCommonColumn();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public interface SqlConformance {
@Deprecated // to be removed before 2.0
SqlConformanceEnum PRAGMATIC_2003 = SqlConformanceEnum.PRAGMATIC_2003;

SqlConformanceEnum HIVE = SqlConformanceEnum.HIVE;
/**
* Whether this dialect supports features from a wide variety of
* dialects. This is enabled for the Babel parser, disabled otherwise.
Expand Down Expand Up @@ -454,6 +455,13 @@ public interface SqlConformance {
*/
boolean allowPluralTimeUnits();

/**
* Whether SELECT can contain a table function.
* <p>For example, consider the query
* <blockquote><pre> SELECT SPLIT(col) AS (F0, F1) FROM A </pre> </blockquote>
*/
boolean allowSelectTableFunction();

/**
* Whether to allow a qualified common column in a query that has a
* NATURAL join or a join with a USING clause.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ public enum SqlConformanceEnum implements SqlConformance {

/** Conformance value that instructs Calcite to use SQL semantics
* consistent with Microsoft SQL Server version 2008. */
SQL_SERVER_2008;
SQL_SERVER_2008,

/** Conformance value that instructs Calcite to use SQL semantics
* consistent with Hive version. */
HIVE;

public boolean isLiberal() {
switch (this) {
Expand Down Expand Up @@ -362,6 +366,15 @@ public boolean allowExtendedTrim() {
}
}

@Override public boolean allowSelectTableFunction() {
switch (this) {
case HIVE:
return true;
default:
return false;
}
}

@Override public boolean allowAliasUnnestItems() {
switch (this) {
case PRESTO:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,7 @@ protected SqlDelegatingConformance(SqlConformance delegate) {
return delegate.allowAliasUnnestItems();
}

@Override public boolean allowSelectTableFunction() {
return delegate.allowSelectTableFunction();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.apache.calcite.runtime.CalciteContextException;
import org.apache.calcite.runtime.CalciteException;
import org.apache.calcite.runtime.Resources;
import org.apache.calcite.sql.SqlBasicCall;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlDataTypeSpec;
import org.apache.calcite.sql.SqlDelete;
Expand Down Expand Up @@ -721,6 +722,14 @@ CalciteException handleUnresolvedFunction(SqlCall call,
*/
SqlValidatorScope getOverScope(SqlNode node);

/**
* Returns the table function SqlBasicCall in SqlSelect.
*
* @param select The select node
* @return The table function node associate with the select node
*/
SqlBasicCall getTableFunctionInSelect(SqlSelect select);

/**
* Validates that a query is capable of producing a return of given modality
* (relational or streaming).
Expand Down
Loading

0 comments on commit 62ee42f

Please sign in to comment.