Skip to content

Commit 956978a

Browse files
mohsakakasiafi
andcommitted
Support table function invocation in grammar and AST
Changes adapted from trino/PR#11336, 12910 Original commit: 2e00c8e64c32d6fdd813999b2e04b3b3415235c8 f0508a7ab420449c6e2960ecf1d0a8d7058242da Author: kasiafi Modifications were made to adapt to Presto including: Addition of KEEP in the parser. Adjustment of the TestSqlParser.java to apply to Presto concepts. Switch from Trino's DataType based datatypes to Presto's String based datatypes. Co-authored-by: kasiafi <[email protected]>
1 parent 85e87a2 commit 956978a

File tree

16 files changed

+1187
-10
lines changed

16 files changed

+1187
-10
lines changed

presto-parser/src/main/antlr4/com/facebook/presto/sql/parser/SqlBase.g4

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ relationPrimary
346346
| UNNEST '(' expression (',' expression)* ')' (WITH ORDINALITY)? #unnest
347347
| LATERAL '(' query ')' #lateral
348348
| '(' relation ')' #parenthesizedRelation
349+
| TABLE '(' tableFunctionCall ')' #tableFunctionInvocation
349350
;
350351

351352
expression
@@ -474,6 +475,40 @@ type
474475
| INTERVAL from=intervalField TO to=intervalField
475476
;
476477

478+
tableFunctionCall
479+
: qualifiedName '(' (tableFunctionArgument (',' tableFunctionArgument)*)?
480+
(COPARTITION copartitionTables (',' copartitionTables)*)? ')'
481+
;
482+
483+
tableFunctionArgument
484+
: (identifier '=>')? (tableArgument | descriptorArgument | expression) // descriptor before expression to avoid parsing descriptor as a function call
485+
;
486+
487+
tableArgument
488+
: tableArgumentRelation
489+
(PARTITION BY ('(' (expression (',' expression)*)? ')' | expression))?
490+
(PRUNE WHEN EMPTY | KEEP WHEN EMPTY)?
491+
(ORDER BY ('(' sortItem (',' sortItem)* ')' | sortItem))?
492+
;
493+
494+
tableArgumentRelation
495+
: TABLE '(' qualifiedName ')' (AS? identifier columnAliases?)? #tableArgumentTable
496+
| TABLE '(' query ')' (AS? identifier columnAliases?)? #tableArgumentQuery
497+
;
498+
499+
descriptorArgument
500+
: DESCRIPTOR '(' descriptorField (',' descriptorField)* ')'
501+
| CAST '(' NULL AS DESCRIPTOR ')'
502+
;
503+
504+
descriptorField
505+
: identifier type?
506+
;
507+
508+
copartitionTables
509+
: '(' qualifiedName ',' qualifiedName (',' qualifiedName)* ')'
510+
;
511+
477512
typeParameter
478513
: INTEGER_VALUE | type
479514
;
@@ -633,20 +668,20 @@ nonReserved
633668
// IMPORTANT: this rule must only contain tokens. Nested rules are not supported. See SqlParser.exitNonReserved
634669
: ADD | ADMIN | ALL | ANALYZE | ANY | ARRAY | ASC | AT
635670
| BEFORE | BERNOULLI
636-
| CALL | CALLED | CASCADE | CATALOGS | COLUMN | COLUMNS | COMMENT | COMMIT | COMMITTED | CURRENT | CURRENT_ROLE
637-
| DATA | DATE | DAY | DEFINER | DESC | DETERMINISTIC | DISABLED | DISTRIBUTED
638-
| ENABLED | ENFORCED | EXCLUDING | EXPLAIN | EXTERNAL
671+
| CALL | CALLED | CASCADE | CATALOGS | COLUMN | COLUMNS | COMMENT | COMMIT | COMMITTED | COPARTITION | CURRENT | CURRENT_ROLE
672+
| DATA | DATE | DAY | DEFINER | DESC | DESCRIPTOR | DETERMINISTIC | DISABLED | DISTRIBUTED
673+
| EMPTY | ENABLED | ENFORCED | EXCLUDING | EXPLAIN | EXTERNAL
639674
| FETCH | FILTER | FIRST | FOLLOWING | FORMAT | FUNCTION | FUNCTIONS
640675
| GRANT | GRANTED | GRANTS | GRAPHVIZ | GROUPS
641676
| HOUR
642677
| IF | IGNORE | INCLUDING | INPUT | INTERVAL | INVOKER | IO | ISOLATION
643678
| JSON
644-
| KEY
679+
| KEEP | KEY
645680
| LANGUAGE | LAST | LATERAL | LEVEL | LIMIT | LOGICAL
646681
| MAP | MATERIALIZED | MINUTE | MONTH
647682
| NAME | NFC | NFD | NFKC | NFKD | NO | NONE | NULLIF | NULLS
648683
| OF | OFFSET | ONLY | OPTION | ORDINALITY | OUTPUT | OVER
649-
| PARTITION | PARTITIONS | POSITION | PRECEDING | PRIMARY | PRIVILEGES | PROPERTIES
684+
| PARTITION | PARTITIONS | POSITION | PRECEDING | PRIMARY | PRIVILEGES | PROPERTIES | PRUNE
650685
| RANGE | READ | REFRESH | RELY | RENAME | REPEATABLE | REPLACE | RESET | RESPECT | RESTRICT | RETURN | RETURNS | REVOKE | ROLE | ROLES | ROLLBACK | ROW | ROWS
651686
| SCHEMA | SCHEMAS | SECOND | SECURITY | SERIALIZABLE | SESSION | SET | SETS | SQL
652687
| SHOW | SOME | START | STATS | SUBSTRING | SYSTEM | SYSTEM_TIME | SYSTEM_VERSION
@@ -686,6 +721,7 @@ COMMIT: 'COMMIT';
686721
COMMITTED: 'COMMITTED';
687722
CONSTRAINT: 'CONSTRAINT';
688723
CREATE: 'CREATE';
724+
COPARTITION: 'COPARTITION';
689725
CROSS: 'CROSS';
690726
CUBE: 'CUBE';
691727
CURRENT: 'CURRENT';
@@ -702,12 +738,14 @@ DEFINER: 'DEFINER';
702738
DELETE: 'DELETE';
703739
DESC: 'DESC';
704740
DESCRIBE: 'DESCRIBE';
741+
DESCRIPTOR: 'DESCRIPTOR';
705742
DETERMINISTIC: 'DETERMINISTIC';
706743
DISABLED: 'DISABLED';
707744
DISTINCT: 'DISTINCT';
708745
DISTRIBUTED: 'DISTRIBUTED';
709746
DROP: 'DROP';
710747
ELSE: 'ELSE';
748+
EMPTY: 'EMPTY';
711749
ENABLED: 'ENABLED';
712750
END: 'END';
713751
ENFORCED: 'ENFORCED';
@@ -755,6 +793,7 @@ IS: 'IS';
755793
ISOLATION: 'ISOLATION';
756794
JSON: 'JSON';
757795
JOIN: 'JOIN';
796+
KEEP: 'KEEP';
758797
KEY: 'KEY';
759798
LANGUAGE: 'LANGUAGE';
760799
LAST: 'LAST';
@@ -802,6 +841,7 @@ PREPARE: 'PREPARE';
802841
PRIMARY: 'PRIMARY';
803842
PRIVILEGES: 'PRIVILEGES';
804843
PROPERTIES: 'PROPERTIES';
844+
PRUNE: 'PRUNE';
805845
RANGE: 'RANGE';
806846
READ: 'READ';
807847
RECURSIVE: 'RECURSIVE';

presto-parser/src/main/java/com/facebook/presto/sql/ExpressionFormatter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ static String formatOrderBy(OrderBy orderBy, Optional<List<Expression>> paramete
738738
return "ORDER BY " + formatSortItems(orderBy.getSortItems(), parameters);
739739
}
740740

741-
static String formatSortItems(List<SortItem> sortItems, Optional<List<Expression>> parameters)
741+
public static String formatSortItems(List<SortItem> sortItems, Optional<List<Expression>> parameters)
742742
{
743743
return Joiner.on(", ").join(sortItems.stream()
744744
.map(sortItemFormatterFunction(parameters))

presto-parser/src/main/java/com/facebook/presto/sql/SqlFormatter.java

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@
112112
import com.facebook.presto.sql.tree.SqlParameterDeclaration;
113113
import com.facebook.presto.sql.tree.StartTransaction;
114114
import com.facebook.presto.sql.tree.Table;
115+
import com.facebook.presto.sql.tree.TableFunctionArgument;
116+
import com.facebook.presto.sql.tree.TableFunctionDescriptorArgument;
117+
import com.facebook.presto.sql.tree.TableFunctionInvocation;
118+
import com.facebook.presto.sql.tree.TableFunctionTableArgument;
115119
import com.facebook.presto.sql.tree.TableSubquery;
116120
import com.facebook.presto.sql.tree.TransactionAccessMode;
117121
import com.facebook.presto.sql.tree.TransactionMode;
@@ -208,6 +212,109 @@ protected Void visitLateral(Lateral node, Integer indent)
208212
return null;
209213
}
210214

215+
@Override
216+
protected Void visitTableFunctionInvocation(TableFunctionInvocation node, Integer indent)
217+
{
218+
append(indent, "TABLE(");
219+
appendTableFunctionInvocation(node, indent + 1);
220+
builder.append(")");
221+
return null;
222+
}
223+
224+
private void appendTableFunctionInvocation(TableFunctionInvocation node, Integer indent)
225+
{
226+
builder.append(formatName(node.getName()))
227+
.append("(\n");
228+
appendTableFunctionArguments(node.getArguments(), indent + 1);
229+
if (!node.getCopartitioning().isEmpty()) {
230+
builder.append("\n");
231+
append(indent + 1, "COPARTITION ");
232+
builder.append(node.getCopartitioning().stream()
233+
.map(tableList -> tableList.stream()
234+
.map(Formatter::formatName)
235+
.collect(Collectors.joining(", ", "(", ")")))
236+
.collect(Collectors.joining(", ")));
237+
}
238+
builder.append(")");
239+
}
240+
241+
private void appendTableFunctionArguments(List<TableFunctionArgument> arguments, int indent)
242+
{
243+
for (int i = 0; i < arguments.size(); i++) {
244+
TableFunctionArgument argument = arguments.get(i);
245+
if (argument.getName().isPresent()) {
246+
append(indent, formatExpression(argument.getName().get(), parameters));
247+
builder.append(" => ");
248+
}
249+
else {
250+
append(indent, "");
251+
}
252+
Node value = argument.getValue();
253+
if (value instanceof Expression) {
254+
builder.append(formatExpression((Expression) value, parameters));
255+
}
256+
else {
257+
process(value, indent + 1);
258+
}
259+
if (i < arguments.size() - 1) {
260+
builder.append(",\n");
261+
}
262+
}
263+
}
264+
265+
@Override
266+
protected Void visitTableArgument(TableFunctionTableArgument node, Integer indent)
267+
{
268+
Relation relation = node.getTable();
269+
Relation unaliased = relation instanceof AliasedRelation ? ((AliasedRelation) relation).getRelation() : relation;
270+
builder.append("TABLE(");
271+
process(unaliased, indent);
272+
builder.append(")");
273+
if (relation instanceof AliasedRelation) {
274+
AliasedRelation aliasedRelation = (AliasedRelation) relation;
275+
builder.append(" AS ")
276+
.append(formatExpression(aliasedRelation.getAlias(), parameters));
277+
appendAliasColumns(builder, aliasedRelation.getColumnNames());
278+
}
279+
if (node.getPartitionBy().isPresent()) {
280+
builder.append("\n");
281+
append(indent, "PARTITION BY ")
282+
.append(node.getPartitionBy().get().stream()
283+
.map(expr -> formatExpression(expr, parameters))
284+
.collect(joining(", ")));
285+
}
286+
node.getEmptyTableTreatment().ifPresent(treatment -> {
287+
builder.append("\n");
288+
append(indent, treatment.getTreatment().name() + " WHEN EMPTY");
289+
});
290+
node.getOrderBy().ifPresent(orderBy -> {
291+
builder.append("\n");
292+
append(indent, formatOrderBy(orderBy, Optional.empty()));
293+
});
294+
return null;
295+
}
296+
297+
@Override
298+
protected Void visitDescriptorArgument(TableFunctionDescriptorArgument node, Integer indent)
299+
{
300+
if (node.getDescriptor().isPresent()) {
301+
builder.append(node.getDescriptor().get().getFields().stream()
302+
.map(field -> {
303+
String formattedField = formatExpression(field.getName(), parameters);
304+
if (field.getType().isPresent()) {
305+
formattedField = formattedField + " " + field.getType().get();
306+
}
307+
return formattedField;
308+
})
309+
.collect(Collectors.joining(", ", "DESCRIPTOR(", ")")));
310+
}
311+
else {
312+
builder.append("CAST (NULL AS DESCRIPTOR)");
313+
}
314+
315+
return null;
316+
}
317+
211318
@Override
212319
protected Void visitPrepare(Prepare node, Integer indent)
213320
{

0 commit comments

Comments
 (0)