Skip to content

Commit 7fb0fc2

Browse files
strongduanmumihaibudiu
authored andcommitted
[CALCITE-6666] Support Oracle SYSDATE and SYSTIMESTAMP functions
1 parent 39652ff commit 7fb0fc2

File tree

9 files changed

+158
-0
lines changed

9 files changed

+158
-0
lines changed

core/src/main/java/org/apache/calcite/DataContext.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ enum Variable {
8282
* statement. Required. */
8383
LOCAL_TIMESTAMP("localTimestamp", Long.class),
8484

85+
/** The time in the operating system time zone of the database server. In
86+
* milliseconds after 1970-01-01 00:00:00, in the time zone of the database
87+
* server. Required. */
88+
SYS_TIMESTAMP("sysTimestamp", Long.class),
89+
8590
/** The Spark engine. Available if Spark is on the class path. */
8691
SPARK_CONTEXT("sparkContext", Object.class),
8792

core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@
299299
import static org.apache.calcite.sql.fun.SqlLibraryOperators.STRCMP;
300300
import static org.apache.calcite.sql.fun.SqlLibraryOperators.STR_TO_MAP;
301301
import static org.apache.calcite.sql.fun.SqlLibraryOperators.SUBSTRING_INDEX;
302+
import static org.apache.calcite.sql.fun.SqlLibraryOperators.SYSDATE;
303+
import static org.apache.calcite.sql.fun.SqlLibraryOperators.SYSTIMESTAMP;
302304
import static org.apache.calcite.sql.fun.SqlLibraryOperators.TAND;
303305
import static org.apache.calcite.sql.fun.SqlLibraryOperators.TANH;
304306
import static org.apache.calcite.sql.fun.SqlLibraryOperators.TIME;
@@ -1212,6 +1214,8 @@ void populate3() {
12121214
define(CURRENT_DATETIME, systemFunctionImplementor);
12131215
define(LOCALTIME, systemFunctionImplementor);
12141216
define(LOCALTIMESTAMP, systemFunctionImplementor);
1217+
define(SYSDATE, systemFunctionImplementor);
1218+
define(SYSTIMESTAMP, systemFunctionImplementor);
12151219

12161220
defineAgg(COUNT, CountImplementor.class);
12171221
defineAgg(REGR_COUNT, CountImplementor.class);
@@ -3804,6 +3808,10 @@ private static class SystemFunctionImplementor
38043808
return Expressions.call(BuiltInMethod.LOCAL_TIMESTAMP.method, root);
38053809
} else if (op == LOCALTIME) {
38063810
return Expressions.call(BuiltInMethod.LOCAL_TIME.method, root);
3811+
} else if (op == SYSDATE) {
3812+
return Expressions.call(BuiltInMethod.SYSDATE.method, root);
3813+
} else if (op == SYSTIMESTAMP) {
3814+
return Expressions.call(BuiltInMethod.SYSTIMESTAMP.method, root);
38073815
} else {
38083816
throw new AssertionError("unknown function " + op);
38093817
}

core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ static class DataContextImpl implements DataContext {
427427
.deriveTimeFrameSet(TimeFrames.CORE);
428428
final long localOffset = timeZone.getOffset(time);
429429
final long currentOffset = localOffset;
430+
final long sysOffset = TimeZone.getDefault().getOffset(time);
430431
final String user = "sa";
431432
final String systemUser = System.getProperty("user.name");
432433
final String localeName = connection.config().locale();
@@ -442,6 +443,7 @@ static class DataContextImpl implements DataContext {
442443
builder.put(Variable.UTC_TIMESTAMP.camelName, time)
443444
.put(Variable.CURRENT_TIMESTAMP.camelName, time + currentOffset)
444445
.put(Variable.LOCAL_TIMESTAMP.camelName, time + localOffset)
446+
.put(Variable.SYS_TIMESTAMP.camelName, time + sysOffset)
445447
.put(Variable.TIME_ZONE.camelName, timeZone)
446448
.put(Variable.TIME_FRAME_SET.camelName, timeFrameSet)
447449
.put(Variable.USER.camelName, user)

core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5387,6 +5387,30 @@ public static int localTime(DataContext root) {
53875387
return timestampToTime(localTimestamp(root));
53885388
}
53895389

5390+
/** SQL {@code SYSTIMESTAMP} function. */
5391+
@NonDeterministic
5392+
public static long sysTimestamp(DataContext root) {
5393+
return DataContext.Variable.SYS_TIMESTAMP.get(root);
5394+
}
5395+
5396+
/** SQL {@code SYSDATE} function.
5397+
*
5398+
* <p> When the date is before 1970-01-01 00:00:00, for example: 1969-12-31 23:59:59,
5399+
* the timestamp will return a negative value, such as -1000. The date(days since epoch)
5400+
* returned by timestampToDate(-1000) is 0, so we need to additionally judge the result
5401+
* of timestampToTime(-1000). If its value is less than 0, we need to reduce date by 1
5402+
* to ensure the accuracy of date. */
5403+
@NonDeterministic
5404+
public static int sysDate(DataContext root) {
5405+
final long timestamp = sysTimestamp(root);
5406+
int date = timestampToDate(timestamp);
5407+
final int time = timestampToTime(timestamp);
5408+
if (time < 0) {
5409+
--date;
5410+
}
5411+
return date;
5412+
}
5413+
53905414
@NonDeterministic
53915415
public static TimeZone timeZone(DataContext root) {
53925416
return DataContext.Variable.TIME_ZONE.get(root);

core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,6 +1023,24 @@ static RelDataType deriveTypeSplit(SqlOperatorBinding operatorBinding,
10231023
SqlFunctionCategory.TIMEDATE)
10241024
.withSyntax(SqlSyntax.FUNCTION_ID);
10251025

1026+
/** The "SYSDATE" function. */
1027+
@LibraryOperator(libraries = {ORACLE})
1028+
public static final SqlFunction SYSDATE =
1029+
SqlBasicFunction.create("SYSDATE",
1030+
ReturnTypes.DATE,
1031+
OperandTypes.NILADIC,
1032+
SqlFunctionCategory.TIMEDATE)
1033+
.withSyntax(SqlSyntax.FUNCTION_ID);
1034+
1035+
/** The "SYSTIMESTAMP" function. */
1036+
@LibraryOperator(libraries = {ORACLE})
1037+
public static final SqlFunction SYSTIMESTAMP =
1038+
SqlBasicFunction.create("SYSTIMESTAMP",
1039+
ReturnTypes.TIMESTAMP_TZ,
1040+
OperandTypes.NILADIC,
1041+
SqlFunctionCategory.TIMEDATE)
1042+
.withSyntax(SqlSyntax.FUNCTION_ID);
1043+
10261044
/** The "DATE_FROM_UNIX_DATE(integer)" function; returns a DATE value
10271045
* a given number of seconds after 1970-01-01. */
10281046
@LibraryOperator(libraries = {BIG_QUERY, SPARK})

core/src/main/java/org/apache/calcite/util/BuiltInMethod.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,8 @@ public enum BuiltInMethod {
778778
String.class),
779779
LOCAL_TIMESTAMP(SqlFunctions.class, "localTimestamp", DataContext.class),
780780
LOCAL_TIME(SqlFunctions.class, "localTime", DataContext.class),
781+
SYSDATE(SqlFunctions.class, "sysDate", DataContext.class),
782+
SYSTIMESTAMP(SqlFunctions.class, "sysTimestamp", DataContext.class),
781783
TIME_ZONE(SqlFunctions.class, "timeZone", DataContext.class),
782784
USER(SqlFunctions.class, "user", DataContext.class),
783785
SYSTEM_USER(SqlFunctions.class, "systemUser", DataContext.class),

core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,6 +1658,50 @@ void testLikeAndSimilarFails() {
16581658
.withOperatorTable(opTable).ok();
16591659
}
16601660

1661+
@Test void testSysDateFunction() {
1662+
final SqlOperatorTable opTable = operatorTableFor(SqlLibrary.ORACLE);
1663+
// test oracle sysdate function validate
1664+
expr("SYSDATE")
1665+
.withOperatorTable(opTable)
1666+
.columnType("DATE NOT NULL");
1667+
expr("^SYSDATE^")
1668+
.fails("Column 'SYSDATE' not found in any table");
1669+
expr("^SYSDATE()^")
1670+
.withOperatorTable(opTable)
1671+
.fails("No match found for function signature SYSDATE..");
1672+
// test oracle sysdate function validate within to_char function
1673+
expr("TO_CHAR(SYSDATE, 'MM-DD-YYYY HH24:MI:SS')")
1674+
.withOperatorTable(opTable)
1675+
.columnType("VARCHAR NOT NULL");
1676+
expr("TO_CHAR(^SYSDATE^, 'MM-DD-YYYY HH24:MI:SS')")
1677+
.fails("Column 'SYSDATE' not found in any table");
1678+
expr("^TO_CHAR(SYSDATE)^")
1679+
.withOperatorTable(opTable)
1680+
.fails("Invalid number of arguments to function 'TO_CHAR'. Was expecting 2 arguments");
1681+
}
1682+
1683+
@Test void testSysTimestampFunction() {
1684+
final SqlOperatorTable opTable = operatorTableFor(SqlLibrary.ORACLE);
1685+
// test oracle systimestamp function validate
1686+
expr("SYSTIMESTAMP")
1687+
.withOperatorTable(opTable)
1688+
.columnType("TIMESTAMP_TZ(0) NOT NULL");
1689+
expr("^SYSTIMESTAMP^")
1690+
.fails("Column 'SYSTIMESTAMP' not found in any table");
1691+
expr("^SYSTIMESTAMP()^")
1692+
.withOperatorTable(opTable)
1693+
.fails("No match found for function signature SYSTIMESTAMP..");
1694+
// test oracle systimestamp function validate within to_char function
1695+
expr("TO_CHAR(SYSTIMESTAMP, 'SSSSS.FF')")
1696+
.withOperatorTable(opTable)
1697+
.columnType("VARCHAR NOT NULL");
1698+
expr("TO_CHAR(^SYSTIMESTAMP^, 'SSSSS.FF')")
1699+
.fails("Column 'SYSTIMESTAMP' not found in any table");
1700+
expr("^TO_CHAR(SYSTIMESTAMP)^")
1701+
.withOperatorTable(opTable)
1702+
.fails("Invalid number of arguments to function 'TO_CHAR'. Was expecting 2 arguments");
1703+
}
1704+
16611705
@Test void testInvalidFunction() {
16621706
wholeExpr("foo()")
16631707
.fails("No match found for function signature FOO..");

site/_docs/reference.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2933,6 +2933,8 @@ In the following:
29332933
| 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
29342934
| b r p | STRPOS(string, substring) | Equivalent to `POSITION(substring IN string)`
29352935
| 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
2936+
| o | SYSDATE | Returns the current date in the operating system time zone of the database server, in a value of datatype DATE.
2937+
| 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.
29362938
| 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.
29372939
| * | TANH(numeric) | Returns the hyperbolic tangent of *numeric*
29382940
| b | TIME(hour, minute, second) | Returns a TIME value *hour*, *minute*, *second* (all of type INTEGER)

testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10434,6 +10434,32 @@ private void testCurrentTimestampFunc(Pair<String, Hook.Closeable> pair) {
1043410434
pair.right.close();
1043510435
}
1043610436

10437+
@Tag("slow")
10438+
@Test void testSysTimestampFuncWithCurrentTime() {
10439+
testSysTimestampFunc(currentTimeString(CURRENT_TZ));
10440+
}
10441+
10442+
@Test void testSysTimestampFuncWithFixedTime() {
10443+
testSysTimestampFunc(fixedTimeString(CURRENT_TZ));
10444+
}
10445+
10446+
private void testSysTimestampFunc(Pair<String, Hook.Closeable> pair) {
10447+
final SqlOperatorFixture f = fixture()
10448+
.setFor(SqlLibraryOperators.SYSTIMESTAMP, VmName.EXPAND)
10449+
.withLibrary(SqlLibrary.ORACLE);
10450+
10451+
f.checkType("SYSTIMESTAMP", "TIMESTAMP_TZ(0) NOT NULL");
10452+
f.checkFails("^SYSTIMESTAMP()^",
10453+
"No match found for function signature SYSTIMESTAMP\\(\\)",
10454+
false);
10455+
// check systimestamp function within cast function
10456+
f.checkType("CAST(SYSTIMESTAMP AS VARCHAR(30))", "VARCHAR(30) NOT NULL");
10457+
f.checkFails("CAST(^SYSTIMESTAMP()^ AS VARCHAR(30))",
10458+
"No match found for function signature SYSTIMESTAMP\\(\\)",
10459+
false);
10460+
pair.right.close();
10461+
}
10462+
1043710463
/**
1043810464
* Returns a time string, in GMT, that will be valid for at least 2 minutes.
1043910465
*
@@ -10524,6 +10550,33 @@ private void testCurrentDateFunc(Pair<String, Hook.Closeable> pair) {
1052410550
}
1052510551
}
1052610552

10553+
@Tag("slow")
10554+
@Test void testSysDateFuncWithCurrentTime() {
10555+
testSysDateFunc(currentTimeString(LOCAL_TZ));
10556+
}
10557+
10558+
@Test void testSysDateFuncWithFixedTime() {
10559+
testSysDateFunc(fixedTimeString(LOCAL_TZ));
10560+
}
10561+
10562+
private void testSysDateFunc(Pair<String, Hook.Closeable> pair) {
10563+
final SqlOperatorFixture f = fixture()
10564+
.setFor(SqlLibraryOperators.SYSDATE)
10565+
.withLibrary(SqlLibrary.ORACLE);
10566+
10567+
f.checkType("SYSDATE", "DATE NOT NULL");
10568+
f.checkType("(SYSDATE - SYSDATE) DAY", "INTERVAL DAY NOT NULL");
10569+
f.checkFails("^SYSDATE()^",
10570+
"No match found for function signature SYSDATE\\(\\)",
10571+
false);
10572+
// check sysdate function within cast function
10573+
f.checkType("CAST(SYSDATE AS VARCHAR(30))", "VARCHAR(30) NOT NULL");
10574+
f.checkFails("CAST(^SYSDATE()^ AS VARCHAR(30))",
10575+
"No match found for function signature SYSDATE\\(\\)",
10576+
false);
10577+
pair.right.close();
10578+
}
10579+
1052710580
@Test void testLastDayFunc() {
1052810581
final SqlOperatorFixture f = fixture();
1052910582
f.setFor(SqlStdOperatorTable.LAST_DAY, VmName.EXPAND);

0 commit comments

Comments
 (0)