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

mbudiu 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 7fb0fc26bd [CALCITE-6666] Support Oracle SYSDATE and SYSTIMESTAMP 
functions
7fb0fc26bd is described below

commit 7fb0fc26bdd067a1ce292cfe1642961fa2d1dc41
Author: Zhengqiang Duan <[email protected]>
AuthorDate: Sat Nov 2 22:09:45 2024 +0800

    [CALCITE-6666] Support Oracle SYSDATE and SYSTIMESTAMP functions
---
 .../main/java/org/apache/calcite/DataContext.java  |  5 ++
 .../calcite/adapter/enumerable/RexImpTable.java    |  8 ++++
 .../apache/calcite/jdbc/CalciteConnectionImpl.java |  2 +
 .../org/apache/calcite/runtime/SqlFunctions.java   | 24 ++++++++++
 .../calcite/sql/fun/SqlLibraryOperators.java       | 18 ++++++++
 .../org/apache/calcite/util/BuiltInMethod.java     |  2 +
 .../org/apache/calcite/test/SqlValidatorTest.java  | 44 ++++++++++++++++++
 site/_docs/reference.md                            |  2 +
 .../org/apache/calcite/test/SqlOperatorTest.java   | 53 ++++++++++++++++++++++
 9 files changed, 158 insertions(+)

diff --git a/core/src/main/java/org/apache/calcite/DataContext.java 
b/core/src/main/java/org/apache/calcite/DataContext.java
index ed784094bc..eeca324268 100644
--- a/core/src/main/java/org/apache/calcite/DataContext.java
+++ b/core/src/main/java/org/apache/calcite/DataContext.java
@@ -82,6 +82,11 @@ public interface DataContext {
      * statement. Required. */
     LOCAL_TIMESTAMP("localTimestamp", Long.class),
 
+    /** The time in the operating system time zone of the database server. In
+     * milliseconds after 1970-01-01 00:00:00, in the time zone of the database
+     * server. Required. */
+    SYS_TIMESTAMP("sysTimestamp", Long.class),
+
     /** The Spark engine. Available if Spark is on the class path. */
     SPARK_CONTEXT("sparkContext", Object.class),
 
diff --git 
a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index 21bca972c4..20680a67b5 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -299,6 +299,8 @@ import static 
org.apache.calcite.sql.fun.SqlLibraryOperators.STARTS_WITH;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.STRCMP;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.STR_TO_MAP;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.SUBSTRING_INDEX;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.SYSDATE;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.SYSTIMESTAMP;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.TAND;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.TANH;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.TIME;
@@ -1212,6 +1214,8 @@ public class RexImpTable {
       define(CURRENT_DATETIME, systemFunctionImplementor);
       define(LOCALTIME, systemFunctionImplementor);
       define(LOCALTIMESTAMP, systemFunctionImplementor);
+      define(SYSDATE, systemFunctionImplementor);
+      define(SYSTIMESTAMP, systemFunctionImplementor);
 
       defineAgg(COUNT, CountImplementor.class);
       defineAgg(REGR_COUNT, CountImplementor.class);
@@ -3804,6 +3808,10 @@ public class RexImpTable {
         return Expressions.call(BuiltInMethod.LOCAL_TIMESTAMP.method, root);
       } else if (op == LOCALTIME) {
         return Expressions.call(BuiltInMethod.LOCAL_TIME.method, root);
+      } else if (op == SYSDATE) {
+        return Expressions.call(BuiltInMethod.SYSDATE.method, root);
+      } else if (op == SYSTIMESTAMP) {
+        return Expressions.call(BuiltInMethod.SYSTIMESTAMP.method, root);
       } else {
         throw new AssertionError("unknown function " + op);
       }
diff --git 
a/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java 
b/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java
index 073b425657..7b69e2c3ed 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java
@@ -427,6 +427,7 @@ abstract class CalciteConnectionImpl
               .deriveTimeFrameSet(TimeFrames.CORE);
       final long localOffset = timeZone.getOffset(time);
       final long currentOffset = localOffset;
+      final long sysOffset = TimeZone.getDefault().getOffset(time);
       final String user = "sa";
       final String systemUser = System.getProperty("user.name");
       final String localeName = connection.config().locale();
@@ -442,6 +443,7 @@ abstract class CalciteConnectionImpl
       builder.put(Variable.UTC_TIMESTAMP.camelName, time)
           .put(Variable.CURRENT_TIMESTAMP.camelName, time + currentOffset)
           .put(Variable.LOCAL_TIMESTAMP.camelName, time + localOffset)
+          .put(Variable.SYS_TIMESTAMP.camelName, time + sysOffset)
           .put(Variable.TIME_ZONE.camelName, timeZone)
           .put(Variable.TIME_FRAME_SET.camelName, timeFrameSet)
           .put(Variable.USER.camelName, user)
diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java 
b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index c9e7eb7c76..980ed5e0fe 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -5387,6 +5387,30 @@ public class SqlFunctions {
     return timestampToTime(localTimestamp(root));
   }
 
+  /** SQL {@code SYSTIMESTAMP} function. */
+  @NonDeterministic
+  public static long sysTimestamp(DataContext root) {
+    return DataContext.Variable.SYS_TIMESTAMP.get(root);
+  }
+
+  /** SQL {@code SYSDATE} function.
+   *
+   * <p> When the date is before 1970-01-01 00:00:00, for example: 1969-12-31 
23:59:59,
+   * the timestamp will return a negative value, such as -1000. The date(days 
since epoch)
+   * returned by timestampToDate(-1000) is 0, so we need to additionally judge 
the result
+   * of timestampToTime(-1000). If its value is less than 0, we need to reduce 
date by 1
+   * to ensure the accuracy of date. */
+  @NonDeterministic
+  public static int sysDate(DataContext root) {
+    final long timestamp = sysTimestamp(root);
+    int date = timestampToDate(timestamp);
+    final int time = timestampToTime(timestamp);
+    if (time < 0) {
+      --date;
+    }
+    return date;
+  }
+
   @NonDeterministic
   public static TimeZone timeZone(DataContext root) {
     return DataContext.Variable.TIME_ZONE.get(root);
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
index 8b16906bd8..f74038718f 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
@@ -1023,6 +1023,24 @@ public abstract class SqlLibraryOperators {
           SqlFunctionCategory.TIMEDATE)
           .withSyntax(SqlSyntax.FUNCTION_ID);
 
+  /** The "SYSDATE" function. */
+  @LibraryOperator(libraries = {ORACLE})
+  public static final SqlFunction SYSDATE =
+      SqlBasicFunction.create("SYSDATE",
+              ReturnTypes.DATE,
+              OperandTypes.NILADIC,
+              SqlFunctionCategory.TIMEDATE)
+          .withSyntax(SqlSyntax.FUNCTION_ID);
+
+  /** The "SYSTIMESTAMP" function. */
+  @LibraryOperator(libraries = {ORACLE})
+  public static final SqlFunction SYSTIMESTAMP =
+      SqlBasicFunction.create("SYSTIMESTAMP",
+              ReturnTypes.TIMESTAMP_TZ,
+              OperandTypes.NILADIC,
+              SqlFunctionCategory.TIMEDATE)
+          .withSyntax(SqlSyntax.FUNCTION_ID);
+
   /** The "DATE_FROM_UNIX_DATE(integer)" function; returns a DATE value
    * a given number of seconds after 1970-01-01. */
   @LibraryOperator(libraries = {BIG_QUERY, SPARK})
diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java 
b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
index 2bad29df0f..6236e0e556 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -778,6 +778,8 @@ public enum BuiltInMethod {
       String.class),
   LOCAL_TIMESTAMP(SqlFunctions.class, "localTimestamp", DataContext.class),
   LOCAL_TIME(SqlFunctions.class, "localTime", DataContext.class),
+  SYSDATE(SqlFunctions.class, "sysDate", DataContext.class),
+  SYSTIMESTAMP(SqlFunctions.class, "sysTimestamp", DataContext.class),
   TIME_ZONE(SqlFunctions.class, "timeZone", DataContext.class),
   USER(SqlFunctions.class, "user", DataContext.class),
   SYSTEM_USER(SqlFunctions.class, "systemUser", DataContext.class),
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 8d2fd0f28f..5a841089f6 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -1658,6 +1658,50 @@ public class SqlValidatorTest extends 
SqlValidatorTestCase {
         .withOperatorTable(opTable).ok();
   }
 
+  @Test void testSysDateFunction() {
+    final SqlOperatorTable opTable = operatorTableFor(SqlLibrary.ORACLE);
+    // test oracle sysdate function validate
+    expr("SYSDATE")
+        .withOperatorTable(opTable)
+        .columnType("DATE NOT NULL");
+    expr("^SYSDATE^")
+        .fails("Column 'SYSDATE' not found in any table");
+    expr("^SYSDATE()^")
+        .withOperatorTable(opTable)
+        .fails("No match found for function signature SYSDATE..");
+    // test oracle sysdate function validate within to_char function
+    expr("TO_CHAR(SYSDATE, 'MM-DD-YYYY HH24:MI:SS')")
+        .withOperatorTable(opTable)
+        .columnType("VARCHAR NOT NULL");
+    expr("TO_CHAR(^SYSDATE^, 'MM-DD-YYYY HH24:MI:SS')")
+        .fails("Column 'SYSDATE' not found in any table");
+    expr("^TO_CHAR(SYSDATE)^")
+        .withOperatorTable(opTable)
+        .fails("Invalid number of arguments to function 'TO_CHAR'. Was 
expecting 2 arguments");
+  }
+
+  @Test void testSysTimestampFunction() {
+    final SqlOperatorTable opTable = operatorTableFor(SqlLibrary.ORACLE);
+    // test oracle systimestamp function validate
+    expr("SYSTIMESTAMP")
+        .withOperatorTable(opTable)
+        .columnType("TIMESTAMP_TZ(0) NOT NULL");
+    expr("^SYSTIMESTAMP^")
+        .fails("Column 'SYSTIMESTAMP' not found in any table");
+    expr("^SYSTIMESTAMP()^")
+        .withOperatorTable(opTable)
+        .fails("No match found for function signature SYSTIMESTAMP..");
+    // test oracle systimestamp function validate within to_char function
+    expr("TO_CHAR(SYSTIMESTAMP, 'SSSSS.FF')")
+        .withOperatorTable(opTable)
+        .columnType("VARCHAR NOT NULL");
+    expr("TO_CHAR(^SYSTIMESTAMP^, 'SSSSS.FF')")
+        .fails("Column 'SYSTIMESTAMP' not found in any table");
+    expr("^TO_CHAR(SYSTIMESTAMP)^")
+        .withOperatorTable(opTable)
+        .fails("Invalid number of arguments to function 'TO_CHAR'. Was 
expecting 2 arguments");
+  }
+
   @Test void testInvalidFunction() {
     wholeExpr("foo()")
         .fails("No match found for function signature FOO..");
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index d26e4710dd..6a5abd3672 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -2933,6 +2933,8 @@ In the following:
 | m | STRCMP(string, string)                         | Returns 0 if both of 
the strings are same and returns -1 when the first argument is smaller than the 
second and 1 when the second one is smaller than the first one
 | b r p | STRPOS(string, substring)                  | Equivalent to 
`POSITION(substring IN string)`
 | b m o p r | SUBSTR(string, position [, substringLength ]) | Returns a 
portion of *string*, beginning at character *position*, *substringLength* 
characters long. SUBSTR calculates lengths using characters as defined by the 
input character set
+| o | SYSDATE                                        | Returns the current 
date in the operating system time zone of the database server, in a value of 
datatype DATE.
+| o | SYSTIMESTAMP                                   | Returns the current 
date and time in the operating system time zone of the database server, in a 
value of datatype TIMESTAMP WITH TIME ZONE.
 | p | TAND(numeric)                                  | Returns the tangent of 
*numeric* in degrees as a double. Returns NaN if *numeric* is NaN. Fails if 
*numeric is greater than the maximum double value.
 | * | TANH(numeric)                                  | Returns the hyperbolic 
tangent of *numeric*
 | b | TIME(hour, minute, second)                     | Returns a TIME value 
*hour*, *minute*, *second* (all of type INTEGER)
diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java 
b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
index 6d46affa08..66ba88520e 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -10434,6 +10434,32 @@ public class SqlOperatorTest {
     pair.right.close();
   }
 
+  @Tag("slow")
+  @Test void testSysTimestampFuncWithCurrentTime() {
+    testSysTimestampFunc(currentTimeString(CURRENT_TZ));
+  }
+
+  @Test void testSysTimestampFuncWithFixedTime() {
+    testSysTimestampFunc(fixedTimeString(CURRENT_TZ));
+  }
+
+  private void testSysTimestampFunc(Pair<String, Hook.Closeable> pair) {
+    final SqlOperatorFixture f = fixture()
+        .setFor(SqlLibraryOperators.SYSTIMESTAMP, VmName.EXPAND)
+        .withLibrary(SqlLibrary.ORACLE);
+
+    f.checkType("SYSTIMESTAMP", "TIMESTAMP_TZ(0) NOT NULL");
+    f.checkFails("^SYSTIMESTAMP()^",
+        "No match found for function signature SYSTIMESTAMP\\(\\)",
+        false);
+    // check systimestamp function within cast function
+    f.checkType("CAST(SYSTIMESTAMP AS VARCHAR(30))", "VARCHAR(30) NOT NULL");
+    f.checkFails("CAST(^SYSTIMESTAMP()^ AS VARCHAR(30))",
+        "No match found for function signature SYSTIMESTAMP\\(\\)",
+        false);
+    pair.right.close();
+  }
+
   /**
    * Returns a time string, in GMT, that will be valid for at least 2 minutes.
    *
@@ -10524,6 +10550,33 @@ public class SqlOperatorTest {
     }
   }
 
+  @Tag("slow")
+  @Test void testSysDateFuncWithCurrentTime() {
+    testSysDateFunc(currentTimeString(LOCAL_TZ));
+  }
+
+  @Test void testSysDateFuncWithFixedTime() {
+    testSysDateFunc(fixedTimeString(LOCAL_TZ));
+  }
+
+  private void testSysDateFunc(Pair<String, Hook.Closeable> pair) {
+    final SqlOperatorFixture f = fixture()
+        .setFor(SqlLibraryOperators.SYSDATE)
+        .withLibrary(SqlLibrary.ORACLE);
+
+    f.checkType("SYSDATE", "DATE NOT NULL");
+    f.checkType("(SYSDATE - SYSDATE) DAY", "INTERVAL DAY NOT NULL");
+    f.checkFails("^SYSDATE()^",
+        "No match found for function signature SYSDATE\\(\\)",
+        false);
+    // check sysdate function within cast function
+    f.checkType("CAST(SYSDATE AS VARCHAR(30))", "VARCHAR(30) NOT NULL");
+    f.checkFails("CAST(^SYSDATE()^ AS VARCHAR(30))",
+        "No match found for function signature SYSDATE\\(\\)",
+        false);
+    pair.right.close();
+  }
+
   @Test void testLastDayFunc() {
     final SqlOperatorFixture f = fixture();
     f.setFor(SqlStdOperatorTable.LAST_DAY, VmName.EXPAND);

Reply via email to