Skip to content

Commit 9ea6d39

Browse files
mohsakakasiafixin-zhang2
committed
Analysis for table and descriptor arguments.
Changes adapted from trino/PR#13602, PR#13653, PR#14115 Original commit: dbef4d9be37494967496230573ab400e54aab0d9 Author: kasiafi Co-authored-by: kasiafi <30203062+kasiafi@users.noreply.github.com> Co-authored-by: Xin Zhang <desertsxin@gmail.com>
1 parent 60416ba commit 9ea6d39

9 files changed

Lines changed: 1440 additions & 118 deletions

File tree

presto-analyzer/src/main/java/com/facebook/presto/sql/analyzer/Analysis.java

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import com.facebook.presto.sql.tree.Offset;
5050
import com.facebook.presto.sql.tree.OrderBy;
5151
import com.facebook.presto.sql.tree.Parameter;
52+
import com.facebook.presto.sql.tree.QualifiedName;
5253
import com.facebook.presto.sql.tree.QuantifiedComparisonExpression;
5354
import com.facebook.presto.sql.tree.Query;
5455
import com.facebook.presto.sql.tree.QuerySpecification;
@@ -214,7 +215,11 @@ public class Analysis
214215

215216
private Optional<List<OutputColumnMetadata>> updatedSourceColumns = Optional.empty();
216217

218+
// names of tables and aliased relations. All names are resolved case-insensitive.
219+
private final Map<NodeRef<Relation>, QualifiedName> relationNames = new LinkedHashMap<>();
217220
private final Map<NodeRef<TableFunctionInvocation>, TableFunctionInvocationAnalysis> tableFunctionAnalyses = new LinkedHashMap<>();
221+
private final Set<NodeRef<Relation>> aliasedRelations = new LinkedHashSet<>();
222+
private final Set<NodeRef<TableFunctionInvocation>> polymorphicTableFunctions = new LinkedHashSet<>();
218223

219224
public Analysis(@Nullable Statement root, Map<NodeRef<Parameter>, Expression> parameters, boolean isDescribe)
220225
{
@@ -1120,6 +1125,36 @@ public TableFunctionInvocationAnalysis getTableFunctionAnalysis(TableFunctionInv
11201125
return tableFunctionAnalyses.get(NodeRef.of(node));
11211126
}
11221127

1128+
public void setRelationName(Relation relation, QualifiedName name)
1129+
{
1130+
relationNames.put(NodeRef.of(relation), name);
1131+
}
1132+
1133+
public QualifiedName getRelationName(Relation relation)
1134+
{
1135+
return relationNames.get(NodeRef.of(relation));
1136+
}
1137+
1138+
public void addAliased(Relation relation)
1139+
{
1140+
aliasedRelations.add(NodeRef.of(relation));
1141+
}
1142+
1143+
public boolean isAliased(Relation relation)
1144+
{
1145+
return aliasedRelations.contains(NodeRef.of(relation));
1146+
}
1147+
1148+
public void addPolymorphicTableFunction(TableFunctionInvocation invocation)
1149+
{
1150+
polymorphicTableFunctions.add(NodeRef.of(invocation));
1151+
}
1152+
1153+
public boolean isPolymorphicTableFunction(TableFunctionInvocation invocation)
1154+
{
1155+
return polymorphicTableFunctions.contains(NodeRef.of(invocation));
1156+
}
1157+
11231158
@Immutable
11241159
public static final class Insert
11251160
{
@@ -1371,23 +1406,229 @@ public int hashCode()
13711406
}
13721407
}
13731408

1409+
public static class TableArgumentAnalysis
1410+
{
1411+
private final String argumentName;
1412+
private final Optional<QualifiedName> name;
1413+
private final Relation relation;
1414+
private final Optional<List<Expression>> partitionBy; // it is allowed to partition by empty list
1415+
private final Optional<OrderBy> orderBy;
1416+
private final boolean pruneWhenEmpty;
1417+
private final boolean rowSemantics;
1418+
private final boolean passThroughColumns;
1419+
1420+
private TableArgumentAnalysis(
1421+
String argumentName,
1422+
Optional<QualifiedName> name,
1423+
Relation relation,
1424+
Optional<List<Expression>> partitionBy,
1425+
Optional<OrderBy> orderBy,
1426+
boolean pruneWhenEmpty,
1427+
boolean rowSemantics,
1428+
boolean passThroughColumns)
1429+
{
1430+
this.argumentName = requireNonNull(argumentName, "argumentName is null");
1431+
this.name = requireNonNull(name, "name is null");
1432+
this.relation = requireNonNull(relation, "relation is null");
1433+
this.partitionBy = requireNonNull(partitionBy, "partitionBy is null").map(ImmutableList::copyOf);
1434+
this.orderBy = requireNonNull(orderBy, "orderBy is null");
1435+
this.pruneWhenEmpty = pruneWhenEmpty;
1436+
this.rowSemantics = rowSemantics;
1437+
this.passThroughColumns = passThroughColumns;
1438+
}
1439+
1440+
public String getArgumentName()
1441+
{
1442+
return argumentName;
1443+
}
1444+
1445+
public Optional<QualifiedName> getName()
1446+
{
1447+
return name;
1448+
}
1449+
1450+
public Relation getRelation()
1451+
{
1452+
return relation;
1453+
}
1454+
1455+
public Optional<List<Expression>> getPartitionBy()
1456+
{
1457+
return partitionBy;
1458+
}
1459+
1460+
public Optional<OrderBy> getOrderBy()
1461+
{
1462+
return orderBy;
1463+
}
1464+
1465+
public boolean isPruneWhenEmpty()
1466+
{
1467+
return pruneWhenEmpty;
1468+
}
1469+
1470+
public boolean isRowSemantics()
1471+
{
1472+
return rowSemantics;
1473+
}
1474+
1475+
public boolean isPassThroughColumns()
1476+
{
1477+
return passThroughColumns;
1478+
}
1479+
1480+
public static Builder builder()
1481+
{
1482+
return new Builder();
1483+
}
1484+
1485+
public static final class Builder
1486+
{
1487+
private String argumentName;
1488+
private Optional<QualifiedName> name = Optional.empty();
1489+
private Relation relation;
1490+
private Optional<List<Expression>> partitionBy = Optional.empty();
1491+
private Optional<OrderBy> orderBy = Optional.empty();
1492+
private boolean pruneWhenEmpty;
1493+
private boolean rowSemantics;
1494+
private boolean passThroughColumns;
1495+
1496+
private Builder() {}
1497+
1498+
public Builder withArgumentName(String argumentName)
1499+
{
1500+
this.argumentName = argumentName;
1501+
return this;
1502+
}
1503+
1504+
public Builder withName(QualifiedName name)
1505+
{
1506+
this.name = Optional.of(name);
1507+
return this;
1508+
}
1509+
1510+
public Builder withRelation(Relation relation)
1511+
{
1512+
this.relation = relation;
1513+
return this;
1514+
}
1515+
1516+
public Builder withPartitionBy(List<Expression> partitionBy)
1517+
{
1518+
this.partitionBy = Optional.of(partitionBy);
1519+
return this;
1520+
}
1521+
1522+
public Builder withOrderBy(OrderBy orderBy)
1523+
{
1524+
this.orderBy = Optional.of(orderBy);
1525+
return this;
1526+
}
1527+
1528+
public Builder withPruneWhenEmpty(boolean pruneWhenEmpty)
1529+
{
1530+
this.pruneWhenEmpty = pruneWhenEmpty;
1531+
return this;
1532+
}
1533+
1534+
public Builder withRowSemantics(boolean rowSemantics)
1535+
{
1536+
this.rowSemantics = rowSemantics;
1537+
return this;
1538+
}
1539+
1540+
public Builder withPassThroughColumns(boolean passThroughColumns)
1541+
{
1542+
this.passThroughColumns = passThroughColumns;
1543+
return this;
1544+
}
1545+
1546+
public TableArgumentAnalysis build()
1547+
{
1548+
return new TableArgumentAnalysis(argumentName, name, relation, partitionBy, orderBy, pruneWhenEmpty, rowSemantics, passThroughColumns);
1549+
}
1550+
}
1551+
}
1552+
13741553
/**
13751554
* Encapsulates the result of analyzing a table function invocation.
13761555
* Includes the connector ID, function name, argument bindings, and the
13771556
* connector-specific table function handle needed for planning and execution.
1557+
*
1558+
* Example of a TableFunctionInvocationAnalysis for a table function
1559+
* with two table arguments, required columns, and co-partitioning
1560+
* implemented by {@link TestingTableFunctions.TwoTableArgumentsFunction}
1561+
*
1562+
* SQL:
1563+
* SELECT * FROM TABLE(system.two_table_arguments_function(
1564+
* input1 => TABLE(t1) PARTITION BY (a, b),
1565+
* input2 => TABLE(SELECT 1, 2) t1(x, y) PARTITION BY (x, y)
1566+
* COPARTITION(t1, s1.t1)))
1567+
*
1568+
* Table Function:
1569+
* super(
1570+
* SCHEMA_NAME,
1571+
* "two_table_arguments_function",
1572+
* ImmutableList.of(
1573+
* TableArgumentSpecification.builder()
1574+
* .name("INPUT1")
1575+
* .build(),
1576+
* TableArgumentSpecification.builder()
1577+
* .name("INPUT2")
1578+
* .build()),
1579+
* GENERIC_TABLE);
1580+
*
1581+
* analyze:
1582+
* return TableFunctionAnalysis.builder()
1583+
* .handle(HANDLE)
1584+
* .returnedType(new Descriptor(ImmutableList.of(new Descriptor.Field(COLUMN_NAME, Optional.of(BOOLEAN)))))
1585+
* .requiredColumns("INPUT1", ImmutableList.of(0))
1586+
* .requiredColumns("INPUT2", ImmutableList.of(0))
1587+
* .build();
1588+
*
1589+
* Example values:
1590+
* connectorId = "tpch"
1591+
* functionName = "two_table_arguments_function"
1592+
* arguments = {
1593+
* "input1" -> row(a bigint,b bigint,c bigint,d bigint),
1594+
* "input2" -> row(x integer,y integer)
1595+
* }
1596+
* tableArgumentAnalyses = [
1597+
* { argumentName="INPUT1", relation=Table{t1}, partitionBy=[a, b], orderBy=[], pruneWhenEmpty=false },
1598+
* { argumentName="INPUT2", relation=AliasedRelation{SELECT 1, 2 AS x, y}, partitionBy=[x, y], orderBy=[], pruneWhenEmpty=false }
1599+
* ]
1600+
* requiredColumns = {
1601+
* "INPUT1" -> [0],
1602+
* "INPUT2" -> [0]
1603+
* }
1604+
* copartitioningLists = [
1605+
* ["INPUT2", "INPUT1"]
1606+
* ]
1607+
* properColumnsCount = 1
1608+
* connectorTableFunctionHandle = TestingTableFunctionPushdownHandle
1609+
* transactionHandle = AbstractAnalyzerTest$1$1
1610+
*
13781611
*/
13791612
public static class TableFunctionInvocationAnalysis
13801613
{
13811614
private final ConnectorId connectorId;
13821615
private final String functionName;
13831616
private final Map<String, Argument> arguments;
1617+
private final List<TableArgumentAnalysis> tableArgumentAnalyses;
1618+
private final Map<String, List<Integer>> requiredColumns;
1619+
private final List<List<String>> copartitioningLists;
1620+
private final int properColumnsCount;
13841621
private final ConnectorTableFunctionHandle connectorTableFunctionHandle;
13851622
private final ConnectorTransactionHandle transactionHandle;
13861623

13871624
public TableFunctionInvocationAnalysis(
13881625
ConnectorId connectorId,
13891626
String functionName,
13901627
Map<String, Argument> arguments,
1628+
List<TableArgumentAnalysis> tableArgumentAnalyses,
1629+
Map<String, List<Integer>> requiredColumns,
1630+
List<List<String>> copartitioningLists,
1631+
int properColumnsCount,
13911632
ConnectorTableFunctionHandle connectorTableFunctionHandle,
13921633
ConnectorTransactionHandle transactionHandle)
13931634
{
@@ -1396,6 +1637,11 @@ public TableFunctionInvocationAnalysis(
13961637
this.arguments = ImmutableMap.copyOf(arguments);
13971638
this.connectorTableFunctionHandle = requireNonNull(connectorTableFunctionHandle, "connectorTableFunctionHandle is null");
13981639
this.transactionHandle = requireNonNull(transactionHandle, "transactionHandle is null");
1640+
this.tableArgumentAnalyses = ImmutableList.copyOf(tableArgumentAnalyses);
1641+
this.requiredColumns = requiredColumns.entrySet().stream()
1642+
.collect(toImmutableMap(Map.Entry::getKey, entry -> ImmutableList.copyOf(entry.getValue())));
1643+
this.copartitioningLists = ImmutableList.copyOf(copartitioningLists);
1644+
this.properColumnsCount = properColumnsCount;
13991645
}
14001646

14011647
public ConnectorId getConnectorId()
@@ -1413,6 +1659,31 @@ public Map<String, Argument> getArguments()
14131659
return arguments;
14141660
}
14151661

1662+
public List<TableArgumentAnalysis> getTableArgumentAnalyses()
1663+
{
1664+
return tableArgumentAnalyses;
1665+
}
1666+
1667+
public Map<String, List<Integer>> getRequiredColumns()
1668+
{
1669+
return requiredColumns;
1670+
}
1671+
1672+
public List<List<String>> getCopartitioningLists()
1673+
{
1674+
return copartitioningLists;
1675+
}
1676+
1677+
/**
1678+
* Proper columns are the columns produced by the table function, as opposed to pass-through columns from input tables.
1679+
* Proper columns should be considered the actual result of the table function.
1680+
* @return the number of table function's proper columns
1681+
*/
1682+
public int getProperColumnsCount()
1683+
{
1684+
return properColumnsCount;
1685+
}
1686+
14161687
public ConnectorTableFunctionHandle getConnectorTableFunctionHandle()
14171688
{
14181689
return connectorTableFunctionHandle;

presto-analyzer/src/main/java/com/facebook/presto/sql/analyzer/SemanticErrorCode.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,6 @@ public enum SemanticErrorCode
5757
INVALID_FUNCTION_NAME,
5858
DUPLICATE_PARAMETER_NAME,
5959
EXCEPTIONS_WHEN_RESOLVING_FUNCTIONS,
60-
61-
MISSING_RETURN_TYPE,
62-
AMBIGUOUS_RETURN_TYPE,
63-
MISSING_ARGUMENT,
64-
INVALID_FUNCTION_ARGUMENT,
65-
INVALID_ARGUMENTS,
66-
NOT_IMPLEMENTED,
67-
6860
ORDER_BY_MUST_BE_IN_SELECT,
6961
ORDER_BY_MUST_BE_IN_AGGREGATE,
7062
REFERENCE_TO_OUTPUT_ATTRIBUTE_WITHIN_ORDER_BY_GROUPING,
@@ -119,4 +111,16 @@ public enum SemanticErrorCode
119111
TOO_MANY_GROUPING_SETS,
120112

121113
INVALID_OFFSET_ROW_COUNT,
114+
115+
TABLE_FUNCTION_MISSING_RETURN_TYPE,
116+
TABLE_FUNCTION_AMBIGUOUS_RETURN_TYPE,
117+
TABLE_FUNCTION_MISSING_ARGUMENT,
118+
TABLE_FUNCTION_INVALID_FUNCTION_ARGUMENT,
119+
TABLE_FUNCTION_INVALID_ARGUMENTS,
120+
TABLE_FUNCTION_IMPLEMENTATION_ERROR,
121+
TABLE_FUNCTION_INVALID_COPARTITIONING,
122+
TABLE_FUNCTION_INVALID_TABLE_FUNCTION_INVOCATION,
123+
TABLE_FUNCTION_DUPLICATE_RANGE_VARIABLE,
124+
TABLE_FUNCTION_INVALID_COLUMN_REFERENCE,
125+
TABLE_FUNCTION_COLUMN_NOT_FOUND
122126
}

presto-main-base/src/main/java/com/facebook/presto/metadata/TableFunctionRegistry.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.facebook.presto.spi.function.SchemaFunctionName;
2222
import com.facebook.presto.spi.function.table.ArgumentSpecification;
2323
import com.facebook.presto.spi.function.table.ConnectorTableFunction;
24+
import com.facebook.presto.spi.function.table.ReturnTypeSpecification.DescribedTable;
2425
import com.facebook.presto.spi.function.table.TableArgumentSpecification;
2526
import com.facebook.presto.sql.analyzer.SemanticException;
2627
import com.facebook.presto.sql.tree.QualifiedName;
@@ -152,5 +153,10 @@ private static void validateTableFunction(ConnectorTableFunction tableFunction)
152153
// The 'keep when empty' or 'prune when empty' property must not be explicitly specified for a table argument with row semantics.
153154
// Such a table argument is implicitly 'prune when empty'. The TableArgumentSpecification.Builder enforces the 'prune when empty' property
154155
// for a table argument with row semantics.
156+
157+
if (tableFunction.getReturnTypeSpecification() instanceof DescribedTable) {
158+
DescribedTable describedTable = (DescribedTable) tableFunction.getReturnTypeSpecification();
159+
checkArgument(describedTable.getDescriptor().isTyped(), "field types missing in returned type specification");
160+
}
155161
}
156162
}

0 commit comments

Comments
 (0)