This is an automated email from the ASF dual-hosted git repository.

snuyanzin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new bf8e1142c8 [CALCITE-4554] Support TIMESTAMP WITH LOCAL TIME ZONE for 
Snapshot and MatchRecognize
bf8e1142c8 is described below

commit bf8e1142c8635823a45117261c836c95a3994668
Author: Sergey Nuyanzin <[email protected]>
AuthorDate: Sun Mar 12 14:41:43 2023 +0100

    [CALCITE-4554] Support TIMESTAMP WITH LOCAL TIME ZONE for Snapshot and 
MatchRecognize
    
    Close apache/calcite#3106
---
 .../java/org/apache/calcite/rel/core/Snapshot.java |  4 +--
 .../calcite/sql/validate/SqlValidatorImpl.java     |  4 +--
 .../org/apache/calcite/test/RelBuilderTest.java    | 25 ++++++++++++++
 .../apache/calcite/test/SqlToRelConverterTest.java |  8 +++++
 .../apache/calcite/test/SqlValidatorMatchTest.kt   | 16 +++++++++
 .../org/apache/calcite/test/SqlValidatorTest.java  |  5 +++
 .../apache/calcite/test/SqlToRelConverterTest.xml  | 38 ++++++++++++++++------
 .../org/apache/calcite/test/catalog/Fixture.java   |  4 +++
 .../test/catalog/MockCatalogReaderSimple.java      |  4 +++
 9 files changed, 94 insertions(+), 14 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/rel/core/Snapshot.java 
b/core/src/main/java/org/apache/calcite/rel/core/Snapshot.java
index 317314d0ad..7a60b9d2f4 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Snapshot.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Snapshot.java
@@ -27,7 +27,7 @@ import org.apache.calcite.rel.hint.RelHint;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexShuttle;
-import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.type.SqlTypeUtil;
 import org.apache.calcite.util.Litmus;
 
 import com.google.common.collect.ImmutableList;
@@ -131,7 +131,7 @@ public abstract class Snapshot extends SingleRel implements 
Hintable {
 
   @Override public boolean isValid(Litmus litmus, @Nullable Context context) {
     RelDataType dataType = period.getType();
-    if (dataType.getSqlTypeName() != SqlTypeName.TIMESTAMP) {
+    if (!SqlTypeUtil.isTimestamp(dataType)) {
       return litmus.fail("The system time period specification expects 
Timestamp type but is '"
           + dataType.getSqlTypeName() + "'");
     }
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
index 5ecb85591f..a16d30862f 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
@@ -5323,7 +5323,7 @@ public class SqlValidatorImpl implements 
SqlValidatorWithHints {
       SqlSnapshot snapshot = (SqlSnapshot) node;
       SqlNode period = snapshot.getPeriod();
       RelDataType dataType = deriveType(requireNonNull(scope, "scope"), 
period);
-      if (dataType.getSqlTypeName() != SqlTypeName.TIMESTAMP) {
+      if (!SqlTypeUtil.isTimestamp(dataType)) {
         throw newValidationError(period,
             
Static.RESOURCE.illegalExpressionForTemporal(dataType.getSqlTypeName().getName()));
       }
@@ -5671,7 +5671,7 @@ public class SqlValidatorImpl implements 
SqlValidatorWithHints {
         identifier = (SqlIdentifier) requireNonNull(firstOrderByColumn, 
"firstOrderByColumn");
       }
       RelDataType firstOrderByColumnType = deriveType(scope, identifier);
-      if (firstOrderByColumnType.getSqlTypeName() != SqlTypeName.TIMESTAMP) {
+      if (!SqlTypeUtil.isTimestamp(firstOrderByColumnType)) {
         throw newValidationError(interval,
           RESOURCE.firstColumnOfOrderByMustBeTimestamp());
       }
diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java 
b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
index b47b101877..0a487b9e7d 100644
--- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
@@ -495,6 +495,31 @@ public class RelBuilderTest {
     assertThat(root, hasTree(expected));
   }
 
+  @Test void testJoinTemporalTableForTimestampWithLocalTimezone() {
+    // Equivalent SQL:
+    //   SELECT *
+    //   FROM orders
+    //   JOIN products_temporal FOR SYSTEM_TIME
+    //   AS OF TIMESTAMP WITH LOCAL TIME ZONE '2011-07-20 12:34:56'
+    //   ON orders.product = products_temporal.id
+    final RelBuilder builder = RelBuilder.create(config().build());
+    RelNode root =
+        builder.scan("orders")
+            .scan("products_temporal")
+            .snapshot(
+                builder.getRexBuilder().makeTimestampWithLocalTimeZoneLiteral(
+                    new TimestampString("2011-07-20 12:34:56"), 0))
+            .join(JoinRelType.INNER,
+                builder.equals(builder.field(2, 0, "PRODUCT"),
+                    builder.field(2, 1, "ID")))
+            .build();
+    final String expected = "LogicalJoin(condition=[=($2, $4)], 
joinType=[inner])\n"
+        + "  LogicalTableScan(table=[[scott, orders]])\n"
+        + "  LogicalSnapshot(period=[2011-07-20 
12:34:56:TIMESTAMP_WITH_LOCAL_TIME_ZONE(0)])\n"
+        + "    LogicalTableScan(table=[[scott, products_temporal]])\n";
+    assertThat(root, hasTree(expected));
+  }
+
   /** Tests that {@link RelBuilder#project} simplifies expressions if and only 
if
    * {@link RelBuilder.Config#simplify}. */
   @Test void testSimplify() {
diff --git 
a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index 9326ae9fc5..3d36135ec6 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -1246,6 +1246,14 @@ class SqlToRelConverterTest extends SqlToRelTestBase {
     sql(sql).ok();
   }
 
+  @Test void testJoinTemporalTableOnSpecificTimestampWithLocalTimeZone() {
+    final String sql = "select stream *\n"
+        + "from orders,\n"
+        + "  products_temporal for system_time as of\n"
+        + "    TIMESTAMP WITH LOCAL TIME ZONE '2011-01-02 00:00:00'";
+    sql(sql).ok();
+  }
+
   @Test void testJoinTemporalTableOnSpecificTime2() {
     // Test temporal table with virtual columns.
     final String sql = "select stream *\n"
diff --git 
a/core/src/test/java/org/apache/calcite/test/SqlValidatorMatchTest.kt 
b/core/src/test/java/org/apache/calcite/test/SqlValidatorMatchTest.kt
index 35bdb99979..678d4f64a9 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorMatchTest.kt
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorMatchTest.kt
@@ -293,4 +293,20 @@ class SqlValidatorMatchTest : SqlValidatorTestCase() {
             """.trimIndent()
         ).fails("First column of ORDER BY must be of type TIMESTAMP")
     }
+
+    @Test
+    fun `match recognize within order by timestamp with local time zone`() {
+        sql(
+            """
+            select *
+            from products_temporal match_recognize (
+                order by sys_start_local_timestamp
+                pattern (strt down+ up+) within ^interval '3:10' minute to 
second^
+                define
+                  down as down.supplierid < PREV(down.supplierid),
+                  up as up.supplierid > prev(up.supplierid)
+              ) mr
+            """.trimIndent()
+        ).ok()
+    }
 }
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index 2ec6d059f8..acd8e3d877 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -7605,6 +7605,11 @@ public class SqlValidatorTest extends 
SqlValidatorTestCase {
         .fails("Table 'PRODUCTS' is not a temporal table, "
             + "can not be queried in system time period specification");
 
+    sql("select stream * from orders, ^products^ for system_time as of"
+        + " TIMESTAMP WITH LOCAL TIME ZONE '2011-01-02 00:00:00'")
+        .fails("Table 'PRODUCTS' is not a temporal table, "
+            + "can not be queried in system time period specification");
+
     sql("select stream * from orders, products_temporal "
         + "for system_time as of ^'2011-01-02 00:00:00'^")
         .fails("The system time period specification expects Timestamp type 
but is 'CHAR'");
diff --git 
a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml 
b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index 450421a3c3..d1581c2922 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -1360,10 +1360,10 @@ cross join lateral (
     <Resource name="plan">
       <![CDATA[
 LogicalDelta
-  LogicalProject(ROWTIME=[$0], PRODUCTID=[$1], ORDERID=[$2], PRODUCTID0=[$3], 
NAME=[$4], SUPPLIERID=[$5], SYS_START=[$6], SYS_END=[$7])
+  LogicalProject(ROWTIME=[$0], PRODUCTID=[$1], ORDERID=[$2], PRODUCTID0=[$3], 
NAME=[$4], SUPPLIERID=[$5], SYS_START=[$6], SYS_END=[$7], 
SYS_START_LOCAL_TIMESTAMP=[$8], SYS_END_LOCAL_TIMESTAMP=[$9])
     LogicalCorrelate(correlation=[$cor1], joinType=[inner], 
requiredColumns=[{0, 1}])
       LogicalTableScan(table=[[CATALOG, SALES, ORDERS]])
-      LogicalProject(PRODUCTID=[$0], NAME=[$1], SUPPLIERID=[$2], 
SYS_START=[$3], SYS_END=[$4])
+      LogicalProject(PRODUCTID=[$0], NAME=[$1], SUPPLIERID=[$2], 
SYS_START=[$3], SYS_END=[$4], SYS_START_LOCAL_TIMESTAMP=[$5], 
SYS_END_LOCAL_TIMESTAMP=[$6])
         LogicalFilter(condition=[=($cor1.PRODUCTID, $0)])
           LogicalSnapshot(period=[$cor1.ROWTIME])
             LogicalTableScan(table=[[CATALOG, SALES, PRODUCTS_TEMPORAL]])
@@ -1383,10 +1383,10 @@ cross join lateral (
     <Resource name="plan">
       <![CDATA[
 LogicalDelta
-  LogicalProject(ROWTIME=[$0], PRODUCTID=[$1], ORDERID=[$2], PRODUCTID0=[$3], 
NAME=[$4], SUPPLIERID=[$5], SYS_START=[$6], SYS_END=[$7])
-    LogicalJoin(condition=[=($1, $8)], joinType=[inner])
+  LogicalProject(ROWTIME=[$0], PRODUCTID=[$1], ORDERID=[$2], PRODUCTID0=[$3], 
NAME=[$4], SUPPLIERID=[$5], SYS_START=[$6], SYS_END=[$7], 
SYS_START_LOCAL_TIMESTAMP=[$8], SYS_END_LOCAL_TIMESTAMP=[$9])
+    LogicalJoin(condition=[=($1, $10)], joinType=[inner])
       LogicalTableScan(table=[[CATALOG, SALES, ORDERS]])
-      LogicalProject(PRODUCTID=[$0], NAME=[$1], SUPPLIERID=[$2], 
SYS_START=[$3], SYS_END=[$4], PRODUCTID5=[$0])
+      LogicalProject(PRODUCTID=[$0], NAME=[$1], SUPPLIERID=[$2], 
SYS_START=[$3], SYS_END=[$4], SYS_START_LOCAL_TIMESTAMP=[$5], 
SYS_END_LOCAL_TIMESTAMP=[$6], PRODUCTID7=[$0])
         LogicalSnapshot(period=[2011-01-02 00:00:00])
           LogicalTableScan(table=[[CATALOG, SALES, PRODUCTS_TEMPORAL]])
 ]]>
@@ -1405,10 +1405,10 @@ cross join lateral (
     <Resource name="plan">
       <![CDATA[
 LogicalDelta
-  LogicalProject(ROWTIME=[$0], PRODUCTID=[$1], ORDERID=[$2], PRODUCTID0=[$3], 
NAME=[$4], SUPPLIERID=[$5], SYS_START=[$6], SYS_END=[$7])
+  LogicalProject(ROWTIME=[$0], PRODUCTID=[$1], ORDERID=[$2], PRODUCTID0=[$3], 
NAME=[$4], SUPPLIERID=[$5], SYS_START=[$6], SYS_END=[$7], 
SYS_START_LOCAL_TIMESTAMP=[$8], SYS_END_LOCAL_TIMESTAMP=[$9])
     LogicalCorrelate(correlation=[$cor0], joinType=[inner], 
requiredColumns=[{0}])
       LogicalTableScan(table=[[CATALOG, SALES, ORDERS]])
-      LogicalProject(PRODUCTID=[$0], NAME=[$1], SUPPLIERID=[$2], 
SYS_START=[$3], SYS_END=[$4])
+      LogicalProject(PRODUCTID=[$0], NAME=[$1], SUPPLIERID=[$2], 
SYS_START=[$3], SYS_END=[$4], SYS_START_LOCAL_TIMESTAMP=[$5], 
SYS_END_LOCAL_TIMESTAMP=[$6])
         LogicalFilter(condition=[>($0, 1)])
           LogicalSnapshot(period=[$cor0.ROWTIME])
             LogicalTableScan(table=[[CATALOG, SALES, PRODUCTS_TEMPORAL]])
@@ -3488,7 +3488,7 @@ on orders.productid = products_temporal.productid]]>
     <Resource name="plan">
       <![CDATA[
 LogicalDelta
-  LogicalProject(ROWTIME=[$0], PRODUCTID=[$1], ORDERID=[$2], PRODUCTID0=[$3], 
NAME=[$4], SUPPLIERID=[$5], SYS_START=[$6], SYS_END=[$7])
+  LogicalProject(ROWTIME=[$0], PRODUCTID=[$1], ORDERID=[$2], PRODUCTID0=[$3], 
NAME=[$4], SUPPLIERID=[$5], SYS_START=[$6], SYS_END=[$7], 
SYS_START_LOCAL_TIMESTAMP=[$8], SYS_END_LOCAL_TIMESTAMP=[$9])
     LogicalCorrelate(correlation=[$cor0], joinType=[inner], 
requiredColumns=[{0, 1}])
       LogicalTableScan(table=[[CATALOG, SALES, ORDERS]])
       LogicalFilter(condition=[=($cor0.PRODUCTID, $0)])
@@ -3527,7 +3527,7 @@ from orders,
     <Resource name="plan">
       <![CDATA[
 LogicalDelta
-  LogicalProject(ROWTIME=[$0], PRODUCTID=[$1], ORDERID=[$2], PRODUCTID0=[$3], 
NAME=[$4], SUPPLIERID=[$5], SYS_START=[$6], SYS_END=[$7])
+  LogicalProject(ROWTIME=[$0], PRODUCTID=[$1], ORDERID=[$2], PRODUCTID0=[$3], 
NAME=[$4], SUPPLIERID=[$5], SYS_START=[$6], SYS_END=[$7], 
SYS_START_LOCAL_TIMESTAMP=[$8], SYS_END_LOCAL_TIMESTAMP=[$9])
     LogicalJoin(condition=[true], joinType=[inner])
       LogicalTableScan(table=[[CATALOG, SALES, ORDERS]])
       LogicalSnapshot(period=[2011-01-02 00:00:00])
@@ -3551,6 +3551,24 @@ LogicalDelta
       LogicalSnapshot(period=[2011-01-02 00:00:00])
         LogicalProject(A=[$0], B=[$1], C=[$2], D=[$3], $f4=[+($0, 1)])
           LogicalTableScan(table=[[CATALOG, VIRTUALCOLUMNS, VC_T1]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJoinTemporalTableOnSpecificTimestampWithLocalTimeZone">
+    <Resource name="sql">
+      <![CDATA[select stream *
+from orders,
+  products_temporal for system_time as of
+    TIMESTAMP WITH LOCAL TIME ZONE '2011-01-02 00:00:00']]>
+    </Resource>
+    <Resource name="plan">
+      <![CDATA[
+LogicalDelta
+  LogicalProject(ROWTIME=[$0], PRODUCTID=[$1], ORDERID=[$2], PRODUCTID0=[$3], 
NAME=[$4], SUPPLIERID=[$5], SYS_START=[$6], SYS_END=[$7], 
SYS_START_LOCAL_TIMESTAMP=[$8], SYS_END_LOCAL_TIMESTAMP=[$9])
+    LogicalJoin(condition=[true], joinType=[inner])
+      LogicalTableScan(table=[[CATALOG, SALES, ORDERS]])
+      LogicalSnapshot(period=[2011-01-02 
00:00:00:TIMESTAMP_WITH_LOCAL_TIME_ZONE(0)])
+        LogicalTableScan(table=[[CATALOG, SALES, PRODUCTS_TEMPORAL]])
 ]]>
     </Resource>
   </TestCase>
@@ -6715,7 +6733,7 @@ LogicalProject(EXPR$0=[$1])
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalProject(PRODUCTID=[$0], NAME=[$1], SUPPLIERID=[$2], SYS_START=[$3], 
SYS_END=[$4])
+LogicalProject(PRODUCTID=[$0], NAME=[$1], SUPPLIERID=[$2], SYS_START=[$3], 
SYS_END=[$4], SYS_START_LOCAL_TIMESTAMP=[$5], SYS_END_LOCAL_TIMESTAMP=[$6])
   LogicalSnapshot(period=[2011-01-02 00:00:00])
     LogicalTableScan(table=[[CATALOG, SALES, PRODUCTS_TEMPORAL]])
 ]]>
diff --git a/testkit/src/main/java/org/apache/calcite/test/catalog/Fixture.java 
b/testkit/src/main/java/org/apache/calcite/test/catalog/Fixture.java
index f1e97dc469..d0df596d2d 100644
--- a/testkit/src/main/java/org/apache/calcite/test/catalog/Fixture.java
+++ b/testkit/src/main/java/org/apache/calcite/test/catalog/Fixture.java
@@ -42,7 +42,11 @@ final class Fixture extends AbstractFixture {
   final RelDataType varchar20Type = sqlType(SqlTypeName.VARCHAR, 20);
   final RelDataType varchar20TypeNull = nullable(varchar20Type);
   final RelDataType timestampType = sqlType(SqlTypeName.TIMESTAMP);
+  final RelDataType timestampTypeWithLocalTimeZone =
+      sqlType(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
   final RelDataType timestampTypeNull = nullable(timestampType);
+  final RelDataType timestampTypeWithLocalTimeZoneNull =
+      nullable(timestampTypeWithLocalTimeZone);
   final RelDataType dateType = sqlType(SqlTypeName.DATE);
   final RelDataType booleanType = sqlType(SqlTypeName.BOOLEAN);
   final RelDataType booleanTypeNull = nullable(booleanType);
diff --git 
a/testkit/src/main/java/org/apache/calcite/test/catalog/MockCatalogReaderSimple.java
 
b/testkit/src/main/java/org/apache/calcite/test/catalog/MockCatalogReaderSimple.java
index ac201378e6..e34173e250 100644
--- 
a/testkit/src/main/java/org/apache/calcite/test/catalog/MockCatalogReaderSimple.java
+++ 
b/testkit/src/main/java/org/apache/calcite/test/catalog/MockCatalogReaderSimple.java
@@ -286,6 +286,10 @@ public class MockCatalogReaderSimple extends 
MockCatalogReader {
     productsTemporalTable.addColumn("SUPPLIERID", fixture.intType);
     productsTemporalTable.addColumn("SYS_START", fixture.timestampType);
     productsTemporalTable.addColumn("SYS_END", fixture.timestampType);
+    productsTemporalTable.addColumn(
+        "SYS_START_LOCAL_TIMESTAMP", fixture.timestampTypeWithLocalTimeZone);
+    productsTemporalTable.addColumn(
+        "SYS_END_LOCAL_TIMESTAMP", fixture.timestampTypeWithLocalTimeZone);
     registerTable(productsTemporalTable);
   }
 

Reply via email to