diff --git a/axiom/sql/presto/ast/AstBuilder.cpp b/axiom/sql/presto/ast/AstBuilder.cpp index ec24af4b5..e5e5c4af0 100644 --- a/axiom/sql/presto/ast/AstBuilder.cpp +++ b/axiom/sql/presto/ast/AstBuilder.cpp @@ -544,6 +544,17 @@ std::any AstBuilder::visitAliasedRelation( std::any AstBuilder::visitTableName(PrestoSqlParser::TableNameContext* ctx) { trace("visitTableName"); + // The grammar accepts `FOR ... AS OF`/`BEFORE`, but no layer below the parser + // reads a chosen snapshot yet. Reject it rather than build a Table that drops + // the clause, which would read the current snapshot as if no version were + // given. + if (ctx->tableVersionExpression() != nullptr) { + AXIOM_PRESTO_SYNTAX_FAIL( + getLocation(ctx->tableVersionExpression()), + ctx->tableVersionExpression()->getText(), + "Table version (time travel) is not supported yet"); + } + return std::static_pointer_cast(std::make_shared( getLocation(ctx), getQualifiedName(ctx->qualifiedName()))); } diff --git a/axiom/sql/presto/tests/PrestoParserTest.cpp b/axiom/sql/presto/tests/PrestoParserTest.cpp index e41621ea9..743ea25cc 100644 --- a/axiom/sql/presto/tests/PrestoParserTest.cpp +++ b/axiom/sql/presto/tests/PrestoParserTest.cpp @@ -1177,6 +1177,24 @@ TEST_F(PrestoParserTest, tablesample) { } } +TEST_F(PrestoParserTest, tableVersion) { + // The grammar accepts a FOR ... AS OF clause, but nothing below the parser + // reads it yet. Rejecting here keeps a versioned reference from silently + // reading the current snapshot. + auto verify = [&](std::string_view version) { + SCOPED_TRACE(version); + AXIOM_EXPECT_PRESTO_SYNTAX_ERROR( + parseSql(fmt::format("SELECT * FROM nation {}", version)), + "Table version (time travel) is not supported yet"); + }; + + verify("FOR VERSION AS OF 8"); + verify("FOR VERSION BEFORE 8"); + verify("FOR SYSTEM_VERSION AS OF 8"); + verify("FOR TIMESTAMP AS OF TIMESTAMP '2020-01-01 00:00:00'"); + verify("FOR SYSTEM_TIME BEFORE TIMESTAMP '2020-01-01 00:00:00'"); +} + TEST_F(PrestoParserTest, everything) { auto matcher = matchScan() .join(matchScan().build())