Skip to content

Commit

Permalink
BEFORE syntax support for presto engine.
Browse files Browse the repository at this point in the history
  • Loading branch information
gupteaj committed May 14, 2024
1 parent e484062 commit d8d60a9
Show file tree
Hide file tree
Showing 10 changed files with 297 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -217,26 +217,31 @@ public void testTableVersionMisc()
@Test
public void testTableVersionErrors()
{
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR VERSION AS OF 100", ".* Type integer is invalid. Supported table version AS OF expression type is BIGINT");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR VERSION AS OF 'bad'", ".* Type varchar\\(3\\) is invalid. Supported table version AS OF expression type is BIGINT");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR VERSION AS OF CURRENT_DATE", ".* Type date is invalid. Supported table version AS OF expression type is BIGINT");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR VERSION AS OF CURRENT_TIMESTAMP", ".* Type timestamp with time zone is invalid. Supported table version AS OF expression type is BIGINT");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR VERSION AS OF 100", ".* Type integer is invalid. Supported table version AS OF/BEFORE expression type is BIGINT");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR VERSION AS OF 'bad'", ".* Type varchar\\(3\\) is invalid. Supported table version AS OF/BEFORE expression type is BIGINT");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR VERSION AS OF CURRENT_DATE", ".* Type date is invalid. Supported table version AS OF/BEFORE expression type is BIGINT");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR VERSION AS OF CURRENT_TIMESTAMP", ".* Type timestamp with time zone is invalid. Supported table version AS OF/BEFORE expression type is BIGINT");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR VERSION AS OF id", ".* cannot be resolved");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR VERSION AS OF (SELECT 10000000)", ".* Constant expression cannot contain a subquery");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR VERSION AS OF NULL", "Table version AS OF expression cannot be NULL for .*");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR VERSION AS OF NULL", "Table version AS OF/BEFORE expression cannot be NULL for .*");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR VERSION AS OF " + tab2VersionId1 + " - " + tab2VersionId1, "Iceberg snapshot ID does not exists: 0");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR VERSION AS OF CAST (100 AS BIGINT)", "Iceberg snapshot ID does not exists: 100");

assertQueryFails("SELECT desc FROM " + tableName2 + " FOR TIMESTAMP AS OF 100", ".* Type integer is invalid. Supported table version AS OF expression type is Timestamp with Time Zone.");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR TIMESTAMP AS OF 'bad'", ".* Type varchar\\(3\\) is invalid. Supported table version AS OF expression type is Timestamp with Time Zone.");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR TIMESTAMP AS OF 100", ".* Type integer is invalid. Supported table version AS OF/BEFORE expression type is Timestamp with Time Zone.");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR TIMESTAMP AS OF 'bad'", ".* Type varchar\\(3\\) is invalid. Supported table version AS OF/BEFORE expression type is Timestamp with Time Zone.");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR TIMESTAMP AS OF id", ".* cannot be resolved");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR TIMESTAMP AS OF (SELECT CURRENT_TIMESTAMP)", ".* Constant expression cannot contain a subquery");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR TIMESTAMP AS OF NULL", "Table version AS OF expression cannot be NULL for .*");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR TIMESTAMP AS OF NULL", "Table version AS OF/BEFORE expression cannot be NULL for .*");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR TIMESTAMP AS OF TIMESTAMP " + "'" + tab2Timestamp1 + "' - INTERVAL '1' MONTH", "No history found based on timestamp for table \"test_tt_schema\".\"test_table_version_tab2\"");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR TIMESTAMP AS OF CAST ('2023-01-01' AS TIMESTAMP WITH TIME ZONE)", "No history found based on timestamp for table \"test_tt_schema\".\"test_table_version_tab2\"");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR TIMESTAMP AS OF CAST ('2023-01-01' AS TIMESTAMP)", ".* Type timestamp is invalid. Supported table version AS OF expression type is Timestamp with Time Zone.");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR TIMESTAMP AS OF CAST ('2023-01-01' AS DATE)", ".* Type date is invalid. Supported table version AS OF expression type is Timestamp with Time Zone.");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR TIMESTAMP AS OF CURRENT_DATE", ".* Type date is invalid. Supported table version AS OF expression type is Timestamp with Time Zone.");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR TIMESTAMP AS OF TIMESTAMP '2023-01-01 00:00:00.000'", ".* Type timestamp is invalid. Supported table version AS OF expression type is Timestamp with Time Zone.");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR TIMESTAMP AS OF CAST ('2023-01-01' AS TIMESTAMP)", ".* Type timestamp is invalid. Supported table version AS OF/BEFORE expression type is Timestamp with Time Zone.");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR TIMESTAMP AS OF CAST ('2023-01-01' AS DATE)", ".* Type date is invalid. Supported table version AS OF/BEFORE expression type is Timestamp with Time Zone.");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR TIMESTAMP AS OF CURRENT_DATE", ".* Type date is invalid. Supported table version AS OF/BEFORE expression type is Timestamp with Time Zone.");
assertQueryFails("SELECT desc FROM " + tableName2 + " FOR TIMESTAMP AS OF TIMESTAMP '2023-01-01 00:00:00.000'", ".* Type timestamp is invalid. Supported table version AS OF/BEFORE expression type is Timestamp with Time Zone.");

assertQueryFails("SELECT desc FROM " + tableName1 + " FOR VERSION BEFORE " + tab1VersionId1 + " ORDER BY 1", ".*Table version BEFORE expression is not supported for .*");
assertQueryFails("SELECT desc FROM " + tableName1 + " FOR SYSTEM_VERSION BEFORE " + tab1VersionId1 + " ORDER BY 1", ".*Table version BEFORE expression is not supported for .*");
assertQueryFails("SELECT desc FROM " + tableName1 + " FOR TIMESTAMP BEFORE TIMESTAMP " + "'" + tab1Timestamp1 + "'" + " ORDER BY 1", ".*Table version BEFORE expression is not supported for .*");
assertQueryFails("SELECT desc FROM " + tableName1 + " FOR SYSTEM_TIME BEFORE TIMESTAMP " + "'" + tab1Timestamp1 + "'" + " ORDER BY 1", ".*Table version BEFORE expression is not supported for .*");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@
import static com.facebook.presto.spi.analyzer.AccessControlRole.TABLE_CREATE;
import static com.facebook.presto.spi.analyzer.AccessControlRole.TABLE_DELETE;
import static com.facebook.presto.spi.analyzer.AccessControlRole.TABLE_INSERT;
import static com.facebook.presto.spi.connector.ConnectorTableVersion.VersionOperator;
import static com.facebook.presto.spi.connector.ConnectorTableVersion.VersionType;
import static com.facebook.presto.spi.function.FunctionKind.AGGREGATE;
import static com.facebook.presto.spi.function.FunctionKind.WINDOW;
Expand Down Expand Up @@ -278,6 +279,9 @@
import static com.facebook.presto.sql.tree.FrameBound.Type.PRECEDING;
import static com.facebook.presto.sql.tree.FrameBound.Type.UNBOUNDED_FOLLOWING;
import static com.facebook.presto.sql.tree.FrameBound.Type.UNBOUNDED_PRECEDING;
import static com.facebook.presto.sql.tree.TableVersionExpression.TableVersionOperator;
import static com.facebook.presto.sql.tree.TableVersionExpression.TableVersionOperator.EQUAL;
import static com.facebook.presto.sql.tree.TableVersionExpression.TableVersionOperator.LESS_THAN;
import static com.facebook.presto.sql.tree.TableVersionExpression.TableVersionType;
import static com.facebook.presto.sql.tree.TableVersionExpression.TableVersionType.TIMESTAMP;
import static com.facebook.presto.sql.tree.TableVersionExpression.TableVersionType.VERSION;
Expand Down Expand Up @@ -1355,7 +1359,7 @@ protected Scope visitTable(Table table, Optional<Scope> scope)

private Optional<TableHandle> getTableHandle(TableColumnMetadata tableColumnsMetadata, Table table, QualifiedObjectName name, Optional<Scope> scope)
{
// Process table version AS OF expression
// Process table version AS OF/BEFORE expression
if (table.getTableVersionExpression().isPresent()) {
return processTableVersion(table, name, scope);
}
Expand All @@ -1364,6 +1368,17 @@ private Optional<TableHandle> getTableHandle(TableColumnMetadata tableColumnsMet
}
}

private VersionOperator toVersionOperator(TableVersionOperator operator)
{
switch (operator) {
case EQUAL:
return VersionOperator.EQUAL;
case LESS_THAN:
return VersionOperator.LESS_THAN;
}
throw new SemanticException(NOT_SUPPORTED, "Table version operator %s not supported." + operator);
}

private VersionType toVersionType(TableVersionType type)
{
switch (type) {
Expand All @@ -1372,35 +1387,39 @@ private VersionType toVersionType(TableVersionType type)
case VERSION:
return VersionType.VERSION;
}
throw new SemanticException(NOT_SUPPORTED, type.toString(), "Table version type not supported.");
throw new SemanticException(NOT_SUPPORTED, "Table version type %s not supported." + type);
}
private Optional<TableHandle> processTableVersion(Table table, QualifiedObjectName name, Optional<Scope> scope)
{
Expression asOfExpr = table.getTableVersionExpression().get().getAsOfExpression();
Expression stateExpr = table.getTableVersionExpression().get().getStateExpression();
TableVersionType tableVersionType = table.getTableVersionExpression().get().getTableVersionType();
ExpressionAnalysis expressionAnalysis = analyzeExpression(asOfExpr, scope.get());
TableVersionOperator tableVersionOperator = table.getTableVersionExpression().get().getTableVersionOperator();
ExpressionAnalysis expressionAnalysis = analyzeExpression(stateExpr, scope.get());
analysis.recordSubqueries(table, expressionAnalysis);
Type asOfExprType = expressionAnalysis.getType(asOfExpr);
if (asOfExprType == UNKNOWN) {
throw new PrestoException(INVALID_ARGUMENTS, format("Table version AS OF expression cannot be NULL for %s", name.toString()));
Type stateExprType = expressionAnalysis.getType(stateExpr);
if (tableVersionOperator == LESS_THAN) {
throw new SemanticException(NOT_SUPPORTED, stateExpr, "Table version BEFORE expression is not supported for %s", name.toString());
}
if (stateExprType == UNKNOWN) {
throw new PrestoException(INVALID_ARGUMENTS, format("Table version AS OF/BEFORE expression cannot be NULL for %s", name.toString()));
}
Object evalAsOfExpr = evaluateConstantExpression(asOfExpr, asOfExprType, metadata, session, analysis.getParameters());
Object evalStateExpr = evaluateConstantExpression(stateExpr, stateExprType, metadata, session, analysis.getParameters());
if (tableVersionType == TIMESTAMP) {
if (!(asOfExprType instanceof TimestampWithTimeZoneType)) {
throw new SemanticException(TYPE_MISMATCH, asOfExpr,
"Type %s is invalid. Supported table version AS OF expression type is Timestamp with Time Zone.",
asOfExprType.getDisplayName());
if (!(stateExprType instanceof TimestampWithTimeZoneType)) {
throw new SemanticException(TYPE_MISMATCH, stateExpr,
"Type %s is invalid. Supported table version AS OF/BEFORE expression type is Timestamp with Time Zone.",
stateExprType.getDisplayName());
}
}
if (tableVersionType == VERSION) {
if (!(asOfExprType instanceof BigintType)) {
throw new SemanticException(TYPE_MISMATCH, asOfExpr,
"Type %s is invalid. Supported table version AS OF expression type is BIGINT",
asOfExprType.getDisplayName());
if (!(stateExprType instanceof BigintType)) {
throw new SemanticException(TYPE_MISMATCH, stateExpr,
"Type %s is invalid. Supported table version AS OF/BEFORE expression type is BIGINT",
stateExprType.getDisplayName());
}
}

ConnectorTableVersion tableVersion = new ConnectorTableVersion(toVersionType(tableVersionType), asOfExprType, evalAsOfExpr);
ConnectorTableVersion tableVersion = new ConnectorTableVersion(toVersionType(tableVersionType), toVersionOperator(tableVersionOperator), stateExprType, evalStateExpr);
return metadata.getHandleVersion(session, name, Optional.of(tableVersion));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,12 @@ qualifiedName
;

tableVersionExpression
: FOR tableVersionType=(SYSTEM_TIME | SYSTEM_VERSION | TIMESTAMP | VERSION) AS OF valueExpression #tableVersion
: FOR tableVersionType=(SYSTEM_TIME | SYSTEM_VERSION | TIMESTAMP | VERSION) tableVersionState valueExpression #tableVersion
;

tableVersionState
: AS OF #tableversionasof
| BEFORE #tableversionbefore
;

grantor
Expand Down Expand Up @@ -622,7 +627,7 @@ constraintEnforced
nonReserved
// IMPORTANT: this rule must only contain tokens. Nested rules are not supported. See SqlParser.exitNonReserved
: ADD | ADMIN | ALL | ANALYZE | ANY | ARRAY | ASC | AT
| BERNOULLI
| BEFORE | BERNOULLI
| CALL | CALLED | CASCADE | CATALOGS | COLUMN | COLUMNS | COMMENT | COMMIT | COMMITTED | CURRENT | CURRENT_ROLE
| DATA | DATE | DAY | DEFINER | DESC | DETERMINISTIC | DISABLED | DISTRIBUTED
| ENABLED | ENFORCED | EXCLUDING | EXPLAIN | EXTERNAL
Expand Down Expand Up @@ -659,6 +664,7 @@ ARRAY: 'ARRAY';
AS: 'AS';
ASC: 'ASC';
AT: 'AT';
BEFORE: 'BEFORE';
BERNOULLI: 'BERNOULLI';
BETWEEN: 'BETWEEN';
BY: 'BY';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
import java.util.function.Function;

import static com.facebook.presto.sql.SqlFormatter.formatSql;
import static com.facebook.presto.sql.tree.TableVersionExpression.TableVersionOperator.EQUAL;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Iterables.getOnlyElement;
import static java.lang.String.format;
Expand Down Expand Up @@ -693,7 +694,9 @@ private String joinExpressions(List<Expression> expressions)

protected String visitTableVersion(TableVersionExpression node, Void context)
{
return "FOR " + node.getTableVersionType().name() + " AS OF " + process(node.getAsOfExpression(), context) + " ";
return "FOR " + node.getTableVersionType().name()
+ (node.getTableVersionOperator() == EQUAL ? " AS OF " : " BEFORE ")
+ process(node.getStateExpression(), context) + " ";
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@
import static com.facebook.presto.sql.tree.RoutineCharacteristics.NullCallClause;
import static com.facebook.presto.sql.tree.RoutineCharacteristics.NullCallClause.CALLED_ON_NULL_INPUT;
import static com.facebook.presto.sql.tree.RoutineCharacteristics.NullCallClause.RETURNS_NULL_ON_NULL_INPUT;
import static com.facebook.presto.sql.tree.TableVersionExpression.TableVersionOperator;
import static com.facebook.presto.sql.tree.TableVersionExpression.TableVersionOperator.EQUAL;
import static com.facebook.presto.sql.tree.TableVersionExpression.TableVersionOperator.LESS_THAN;
import static com.facebook.presto.sql.tree.TableVersionExpression.timestampExpression;
import static com.facebook.presto.sql.tree.TableVersionExpression.versionExpression;
import static com.google.common.collect.ImmutableList.toImmutableList;
Expand Down Expand Up @@ -1601,10 +1604,10 @@ public Node visitTableVersion(SqlBaseParser.TableVersionContext context)
switch (context.tableVersionType.getType()) {
case SqlBaseLexer.SYSTEM_TIME:
case SqlBaseLexer.TIMESTAMP:
return timestampExpression(getLocation(context), child);
return timestampExpression(getLocation(context), getTableVersionOperator((Token) context.tableVersionState().getChild(0).getPayload()), child);
case SqlBaseLexer.SYSTEM_VERSION:
case SqlBaseLexer.VERSION:
return versionExpression(getLocation(context), child);
return versionExpression(getLocation(context), getTableVersionOperator((Token) context.tableVersionState().getChild(0).getPayload()), child);
default:
throw new UnsupportedOperationException("Unsupported Type: " + context.tableVersionType.getText());
}
Expand Down Expand Up @@ -2347,6 +2350,18 @@ private static ConstraintType getConstraintType(Token token)
throw new IllegalArgumentException("Unsupported constraint type: " + token.getText());
}

private static TableVersionOperator getTableVersionOperator(Token token)
{
switch (token.getType()) {
case SqlBaseLexer.AS:
return EQUAL;
case SqlBaseLexer.BEFORE:
return LESS_THAN;
}

throw new IllegalArgumentException("Unsupported table version operator: " + token.getText());
}

private static ArithmeticBinaryExpression.Operator getArithmeticBinaryOperator(Token operator)
{
switch (operator.getType()) {
Expand Down
Loading

0 comments on commit d8d60a9

Please sign in to comment.