Skip to content

Commit

Permalink
Support table function invocation in grammar and AST
Browse files Browse the repository at this point in the history
  • Loading branch information
kasiafi committed May 10, 2022
1 parent f16c90a commit ee0bb3b
Show file tree
Hide file tree
Showing 14 changed files with 1,042 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@
import io.trino.sql.tree.SubscriptExpression;
import io.trino.sql.tree.Table;
import io.trino.sql.tree.TableExecute;
import io.trino.sql.tree.TableFunctionInvocation;
import io.trino.sql.tree.TableSubquery;
import io.trino.sql.tree.TruncateTable;
import io.trino.sql.tree.Union;
Expand Down Expand Up @@ -1437,6 +1438,12 @@ protected Scope visitLateral(Lateral node, Optional<Scope> scope)
return createAndAssignScope(node, scope, queryScope.getRelationType());
}

@Override
protected Scope visitTableFunctionInvocation(TableFunctionInvocation node, Optional<Scope> scope)
{
throw semanticException(NOT_SUPPORTED, node, "table functions are not yet supported");
}

private Optional<QualifiedName> getMaterializedViewStorageTableName(MaterializedViewDefinition viewDefinition)
{
if (viewDefinition.getStorageTable().isEmpty()) {
Expand Down
46 changes: 43 additions & 3 deletions core/trino-parser/src/main/antlr4/io/trino/sql/parser/SqlBase.g4
Original file line number Diff line number Diff line change
Expand Up @@ -405,9 +405,44 @@ relationPrimary
| '(' query ')' #subqueryRelation
| UNNEST '(' expression (',' expression)* ')' (WITH ORDINALITY)? #unnest
| LATERAL '(' query ')' #lateral
| TABLE '(' tableFunctionCall ')' #tableFunctionInvocation
| '(' relation ')' #parenthesizedRelation
;

tableFunctionCall
: qualifiedName '(' (tableFunctionArgument (',' tableFunctionArgument)*)?
(COPARTITION copartitionTables (',' copartitionTables)*)? ')'
;

tableFunctionArgument
: (identifier '=>')? (tableArgument | descriptorArgument | expression) // descriptor before expression to avoid parsing descriptor as a function call
;

tableArgument
: tableArgumentRelation
(PARTITION BY ('(' (expression (',' expression)*)? ')' | expression))?
(PRUNE WHEN EMPTY | KEEP WHEN EMPTY)?
(ORDER BY ('(' sortItem (',' sortItem)* ')' | sortItem))?
;

tableArgumentRelation
: TABLE '(' qualifiedName ')' (AS? identifier columnAliases?)? #tableArgumentTable
| TABLE '(' query ')' (AS? identifier columnAliases?)? #tableArgumentQuery
;

descriptorArgument
: DESCRIPTOR '(' descriptorField (',' descriptorField)* ')'
| CAST '(' NULL AS DESCRIPTOR ')'
;

descriptorField
: identifier type?
;

copartitionTables
: '(' qualifiedName ',' qualifiedName (',' qualifiedName)* ')'
;

expression
: booleanExpression
;
Expand Down Expand Up @@ -717,19 +752,20 @@ nonReserved
// IMPORTANT: this rule must only contain tokens. Nested rules are not supported. See SqlParser.exitNonReserved
: ADD | ADMIN | AFTER | ALL | ANALYZE | ANY | ARRAY | ASC | AT | AUTHORIZATION
| BERNOULLI | BOTH
| CALL | CASCADE | CATALOGS | COLUMN | COLUMNS | COMMENT | COMMIT | COMMITTED | COUNT | CURRENT
| DATA | DATE | DAY | DEFAULT | DEFINE | DEFINER | DESC | DISTRIBUTED | DOUBLE
| CALL | CASCADE | CATALOGS | COLUMN | COLUMNS | COMMENT | COMMIT | COMMITTED | COPARTITION | COUNT | CURRENT
| DATA | DATE | DAY | DEFAULT | DEFINE | DEFINER | DESC | DESCRIPTOR | DISTRIBUTED | DOUBLE
| EMPTY | ERROR | EXCLUDING | EXPLAIN
| FETCH | FILTER | FINAL | FIRST | FOLLOWING | FORMAT | FUNCTIONS
| GRANT | DENY | GRANTED | GRANTS | GRAPHVIZ | GROUPS
| HOUR
| IF | IGNORE | INCLUDING | INITIAL | INPUT | INTERVAL | INVOKER | IO | ISOLATION
| JSON
| KEEP
| LAST | LATERAL | LEADING | LEVEL | LIMIT | LOCAL | LOGICAL
| MAP | MATCH | MATCHED | MATCHES | MATCH_RECOGNIZE | MATERIALIZED | MEASURES | MERGE | MINUTE | MONTH
| NEXT | NFC | NFD | NFKC | NFKD | NO | NONE | NULLIF | NULLS
| OF | OFFSET | OMIT | ONE | ONLY | OPTION | ORDINALITY | OUTPUT | OVER | OVERFLOW
| PARTITION | PARTITIONS | PAST | PATH | PATTERN | PER | PERMUTE | POSITION | PRECEDING | PRECISION | PRIVILEGES | PROPERTIES
| PARTITION | PARTITIONS | PAST | PATH | PATTERN | PER | PERMUTE | POSITION | PRECEDING | PRECISION | PRIVILEGES | PROPERTIES | PRUNE
| RANGE | READ | REFRESH | RENAME | REPEATABLE | REPLACE | RESET | RESPECT | RESTRICT | REVOKE | ROLE | ROLES | ROLLBACK | ROW | ROWS | RUNNING
| SCHEMA | SCHEMAS | SECOND | SECURITY | SEEK | SERIALIZABLE | SESSION | SET | SETS
| SHOW | SOME | START | STATS | SUBSET | SUBSTRING | SYSTEM
Expand Down Expand Up @@ -770,6 +806,7 @@ COMMIT: 'COMMIT';
COMMITTED: 'COMMITTED';
CONSTRAINT: 'CONSTRAINT';
COUNT: 'COUNT';
COPARTITION: 'COPARTITION';
CREATE: 'CREATE';
CROSS: 'CROSS';
CUBE: 'CUBE';
Expand All @@ -792,6 +829,7 @@ DELETE: 'DELETE';
DENY: 'DENY';
DESC: 'DESC';
DESCRIBE: 'DESCRIBE';
DESCRIPTOR: 'DESCRIPTOR';
DEFINE: 'DEFINE';
DISTINCT: 'DISTINCT';
DISTRIBUTED: 'DISTRIBUTED';
Expand Down Expand Up @@ -845,6 +883,7 @@ IS: 'IS';
ISOLATION: 'ISOLATION';
JOIN: 'JOIN';
JSON: 'JSON';
KEEP: 'KEEP';
LAST: 'LAST';
LATERAL: 'LATERAL';
LEADING: 'LEADING';
Expand Down Expand Up @@ -907,6 +946,7 @@ PRECISION: 'PRECISION';
PREPARE: 'PREPARE';
PRIVILEGES: 'PRIVILEGES';
PROPERTIES: 'PROPERTIES';
PRUNE: 'PRUNE';
RANGE: 'RANGE';
READ: 'READ';
RECURSIVE: 'RECURSIVE';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -904,7 +904,7 @@ public static String formatOrderBy(OrderBy orderBy)
return "ORDER BY " + formatSortItems(orderBy.getSortItems());
}

private static String formatSortItems(List<SortItem> sortItems)
public static String formatSortItems(List<SortItem> sortItems)
{
return Joiner.on(", ").join(sortItems.stream()
.map(sortItemFormatterFunction())
Expand Down
112 changes: 112 additions & 0 deletions core/trino-parser/src/main/java/io/trino/sql/SqlFormatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import io.trino.sql.tree.Deny;
import io.trino.sql.tree.DescribeInput;
import io.trino.sql.tree.DescribeOutput;
import io.trino.sql.tree.DescriptorArgument;
import io.trino.sql.tree.DropColumn;
import io.trino.sql.tree.DropMaterializedView;
import io.trino.sql.tree.DropRole;
Expand Down Expand Up @@ -120,7 +121,10 @@
import io.trino.sql.tree.SingleColumn;
import io.trino.sql.tree.StartTransaction;
import io.trino.sql.tree.Table;
import io.trino.sql.tree.TableArgument;
import io.trino.sql.tree.TableExecute;
import io.trino.sql.tree.TableFunctionArgument;
import io.trino.sql.tree.TableFunctionInvocation;
import io.trino.sql.tree.TableSubquery;
import io.trino.sql.tree.TransactionAccessMode;
import io.trino.sql.tree.TransactionMode;
Expand Down Expand Up @@ -227,6 +231,114 @@ protected Void visitLateral(Lateral node, Integer indent)
return null;
}

@Override
protected Void visitTableFunctionInvocation(TableFunctionInvocation node, Integer indent)
{
append(indent, "TABLE(");
appendTableFunctionInvocation(node, indent + 1);
builder.append(")");
return null;
}

private void appendTableFunctionInvocation(TableFunctionInvocation node, Integer indent)
{
builder.append(formatName(node.getName()))
.append("(\n");
appendTableFunctionArguments(node.getArguments(), indent + 1);
if (!node.getCopartitioning().isEmpty()) {
builder.append("\n");
append(indent + 1, "COPARTITION ");
builder.append(node.getCopartitioning().stream()
.map(tableList -> tableList.stream()
.map(SqlFormatter::formatName)
.collect(Collectors.joining(", ", "(", ")")))
.collect(Collectors.joining(", ")));
}
builder.append(")");
}

private void appendTableFunctionArguments(List<TableFunctionArgument> arguments, int indent)
{
for (int i = 0; i < arguments.size(); i++) {
TableFunctionArgument argument = arguments.get(i);
if (argument.getName().isPresent()) {
append(indent, formatExpression(argument.getName().get()));
builder.append(" => ");
}
else {
append(indent, "");
}
Node value = argument.getValue();
if (value instanceof Expression) {
builder.append(formatExpression((Expression) value));
}
else {
process(value, indent + 1);
}
if (i < arguments.size() - 1) {
builder.append(",\n");
}
}
}

@Override
protected Void visitTableArgument(TableArgument node, Integer indent)
{
Relation relation = node.getTable();
Relation unaliased = relation instanceof AliasedRelation ? ((AliasedRelation) relation).getRelation() : relation;
builder.append("TABLE(");
process(unaliased, indent);
builder.append(")");
if (relation instanceof AliasedRelation) {
AliasedRelation aliasedRelation = (AliasedRelation) relation;
builder.append(" AS ")
.append(formatExpression(aliasedRelation.getAlias()));
appendAliasColumns(builder, aliasedRelation.getColumnNames());
}
if (node.getPartitionBy().isPresent()) {
builder.append("\n");
append(indent, "PARTITION BY ")
.append(node.getPartitionBy().get().stream()
.map(ExpressionFormatter::formatExpression)
.collect(joining(", ")));
}
if (node.isPruneWhenEmpty()) {
builder.append("\n");
append(indent, "PRUNE WHEN EMPTY");
}
else {
builder.append("\n");
append(indent, "KEEP WHEN EMPTY");
}
node.getOrderBy().ifPresent(orderBy -> {
builder.append("\n");
append(indent, formatOrderBy(orderBy));
});

return null;
}

@Override
protected Void visitDescriptorArgument(DescriptorArgument node, Integer indent)
{
if (node.getDescriptor().isPresent()) {
builder.append(node.getDescriptor().get().getFields().stream()
.map(field -> {
String formattedField = formatExpression(field.getName());
if (field.getType().isPresent()) {
formattedField = formattedField + " " + formatExpression(field.getType().get());
}
return formattedField;
})
.collect(Collectors.joining(", ", "DESCRIPTOR(", ")")));
}
else {
builder.append("CAST (NULL AS DESCRIPTOR)");
}

return null;
}

@Override
protected Void visitPrepare(Prepare node, Integer indent)
{
Expand Down
Loading

0 comments on commit ee0bb3b

Please sign in to comment.