This is an automated email from the ASF dual-hosted git repository. dkuzmenko pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/hive.git
The following commit(s) were added to refs/heads/master by this push: new 99aa3995609 HIVE-26995: Iceberg: Enhance time travel syntax with expressions (Denys Kuzmenko, reviewed by Krisztian Kasa) 99aa3995609 is described below commit 99aa3995609a47be7f6f34c644c73e50f36dfd24 Author: Denys Kuzmenko <denisk...@gmail.com> AuthorDate: Mon Jan 30 11:31:48 2023 +0200 HIVE-26995: Iceberg: Enhance time travel syntax with expressions (Denys Kuzmenko, reviewed by Krisztian Kasa) Closes #3988 --- .../iceberg/mr/hive/TestHiveIcebergTimeTravel.java | 24 ++++++++++++++++++++++ .../apache/hadoop/hive/ql/parse/FromClauseParser.g | 2 +- .../apache/hadoop/hive/ql/io/HiveInputFormat.java | 4 +--- .../hadoop/hive/ql/parse/SemanticAnalyzer.java | 13 +++++++++++- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/TestHiveIcebergTimeTravel.java b/iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/TestHiveIcebergTimeTravel.java index 13cfd975f4d..be865817d13 100644 --- a/iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/TestHiveIcebergTimeTravel.java +++ b/iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/TestHiveIcebergTimeTravel.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.util.List; import org.apache.iceberg.HistoryEntry; import org.apache.iceberg.Table; +import org.apache.iceberg.types.Types; import org.junit.Assert; import org.junit.Test; @@ -117,6 +118,29 @@ public class TestHiveIcebergTimeTravel extends HiveIcebergStorageHandlerWithEngi Assert.assertEquals(7, rows.size()); } + @Test + public void testSelectAsOfCurrentTimestampAndInterval() throws IOException, InterruptedException { + testTables.createTableWithVersions(shell, "customers", + HiveIcebergStorageHandlerTestUtils.CUSTOMER_SCHEMA, fileFormat, + HiveIcebergStorageHandlerTestUtils.CUSTOMER_RECORDS, 3); + + List<Object[]> rows = shell.executeStatement("SELECT * FROM " + + "customers FOR SYSTEM_TIME AS OF CURRENT_TIMESTAMP + interval '10' hours"); + + Assert.assertEquals(5, rows.size()); + } + + @Test + public void testInvalidSelectAsOfTimestampExpression() throws IOException, InterruptedException { + Table icebergTable = testTables.createTableWithVersions(shell, "customers", + HiveIcebergStorageHandlerTestUtils.CUSTOMER_SCHEMA, fileFormat, + HiveIcebergStorageHandlerTestUtils.CUSTOMER_RECORDS, 3); + icebergTable.updateSchema().addColumn("create_time", Types.TimestampType.withZone()).commit(); + + Assert.assertThrows(IllegalArgumentException.class, () -> shell.executeStatement("SELECT * FROM " + + "customers FOR SYSTEM_TIME AS OF create_time - interval '10' hours")); + } + @Test public void testAsOfWithJoins() throws IOException, InterruptedException { Table table = testTables.createTableWithVersions(shell, "customers", diff --git a/parser/src/java/org/apache/hadoop/hive/ql/parse/FromClauseParser.g b/parser/src/java/org/apache/hadoop/hive/ql/parse/FromClauseParser.g index dbad0f7f33b..abeb38305dc 100644 --- a/parser/src/java/org/apache/hadoop/hive/ql/parse/FromClauseParser.g +++ b/parser/src/java/org/apache/hadoop/hive/ql/parse/FromClauseParser.g @@ -217,7 +217,7 @@ asOfClause @init { gParent.pushMsg("as of system_time / system_version clause for table", state); } @after { gParent.popMsg(state); } : - (KW_FOR KW_SYSTEM_TIME KW_AS KW_OF asOfTime=StringLiteral) + (KW_FOR KW_SYSTEM_TIME KW_AS KW_OF asOfTime=expression) -> ^(TOK_AS_OF_TIME $asOfTime) | (KW_FOR KW_SYSTEM_VERSION KW_AS KW_OF asOfVersion=Number) diff --git a/ql/src/java/org/apache/hadoop/hive/ql/io/HiveInputFormat.java b/ql/src/java/org/apache/hadoop/hive/ql/io/HiveInputFormat.java index de93573e303..951e20687a0 100755 --- a/ql/src/java/org/apache/hadoop/hive/ql/io/HiveInputFormat.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/io/HiveInputFormat.java @@ -52,7 +52,6 @@ import org.apache.hadoop.hive.ql.plan.ExprNodeGenericFuncDesc; import org.apache.hadoop.hive.ql.plan.MapWork; import org.apache.hadoop.hive.ql.plan.OperatorDesc; import org.apache.hadoop.hive.ql.plan.PartitionDesc; -import org.apache.hadoop.hive.ql.plan.PlanUtils; import org.apache.hadoop.hive.ql.plan.TableDesc; import org.apache.hadoop.hive.ql.plan.TableScanDesc; import org.apache.hadoop.hive.ql.session.SessionState; @@ -987,8 +986,7 @@ public class HiveInputFormat<K extends WritableComparable, V extends Writable> if (scanDesc.getAsOfTimestamp() != null) { ZoneId timeZone = SessionState.get() == null ? new HiveConf().getLocalTimeZone() : SessionState.get().getConf().getLocalTimeZone(); - TimestampTZ time = TimestampTZUtil.parse(PlanUtils.stripQuotes(scanDesc.getAsOfTimestamp()), timeZone); - + TimestampTZ time = TimestampTZUtil.parse(scanDesc.getAsOfTimestamp(), timeZone); jobConf.set(TableScanDesc.AS_OF_TIMESTAMP, Long.toString(time.toEpochMilli())); } diff --git a/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java b/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java index 6053f3bc90e..e460ae1df8c 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java @@ -1133,7 +1133,18 @@ public class SemanticAnalyzer extends BaseSemanticAnalyzer { if (asOfTimeIndex != -1 || asOfVersionIndex != -1) { String asOfVersion = asOfVersionIndex == -1 ? null : tabref.getChild(asOfVersionIndex).getChild(0).getText(); - String asOfTime = asOfTimeIndex == -1 ? null : tabref.getChild(asOfTimeIndex).getChild(0).getText(); + String asOfTime = null; + + if (asOfTimeIndex != -1) { + ASTNode expr = (ASTNode) tabref.getChild(asOfTimeIndex).getChild(0); + if (expr.getChildCount() > 0) { + ExprNodeDesc desc = genExprNodeDesc(expr, new RowResolver(), false, true); + ExprNodeConstantDesc c = (ExprNodeConstantDesc) desc; + asOfTime = String.valueOf(c.getValue()); + } else { + asOfTime = stripQuotes(expr.getText()); + } + } Pair<String, String> asOf = Pair.of(asOfVersion, asOfTime); qb.setAsOf(alias, asOf); }