Skip to content

Commit

Permalink
[CALCITE-6021] Add CURRENT_DATETIME function (enabled in BigQuery lib…
Browse files Browse the repository at this point in the history
…rary)

Co-authored-by: Julian Hyde <jhyde@apache.org>
Co-authored-by: Tanner Clary <tannerclary@google.com>
  • Loading branch information
julianhyde and tanclary committed Oct 12, 2023
1 parent 77b3689 commit db60219
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 34 deletions.
56 changes: 34 additions & 22 deletions babel/src/test/resources/sql/big-query.iq
Original file line number Diff line number Diff line change
Expand Up @@ -148,23 +148,25 @@ SELECT current_date() AS the_date, t.current_date FROM t;
#
# Returns DATETIME

!if (false) {
SELECT CURRENT_DATETIME() as now;
+----------------------------+
| now |
+----------------------------+
| 2016-05-19T10:38:47.046465 |
+----------------------------+
SELECT CURRENT_DATETIME() > DATETIME '2008-12-25 15:30:00' as now;
+------+
| now |
+------+
| true |
+------+
(1 row)

!ok

SELECT CURRENT_DATETIME as now;
+----------------------------+
| now |
+----------------------------+
| 2016-05-19T10:38:47.046465 |
+----------------------------+
SELECT CURRENT_DATETIME > DATETIME '2008-12-25 15:30:00' as now;
+------+
| now |
+------+
| true |
+------+
(1 row)

!ok
!}

# When a column named current_datetime is present, the column name and
# the function call without parentheses are ambiguous. To ensure the
Expand All @@ -173,16 +175,26 @@ SELECT CURRENT_DATETIME as now;
# select the function in the now column and the table column in the
# current_datetime column.

!if (false) {
WITH t AS (SELECT 'column value' AS `current_datetime`)
SELECT current_datetime() as now, t.current_datetime FROM t;
+----------------------------+------------------+
| now | current_datetime |
+----------------------------+------------------+
| 2016-05-19T10:38:47.046465 | column value |
+----------------------------+------------------+
SELECT current_datetime() > DATETIME '2008-12-25 15:30:00' as now, t.current_datetime FROM t;
+------+------------------+
| now | current_datetime |
+------+------------------+
| true | column value |
+------+------------------+
(1 row)

!ok

SELECT CURRENT_DATETIME('UTC') > DATETIME '2008-12-25 15:30:00';
+--------+
| EXPR$0 |
+--------+
| true |
+--------+
(1 row)

!ok
!}

#####################################################################
# CURRENT_TIME
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@
import static org.apache.calcite.sql.fun.SqlLibraryOperators.COTH;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.CSC;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.CSCH;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.CURRENT_DATETIME;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.DATE;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.DATEADD;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.DATETIME;
Expand Down Expand Up @@ -1002,6 +1003,7 @@ Builder populate3() {
map.put(CURRENT_TIME, systemFunctionImplementor);
map.put(CURRENT_TIMESTAMP, systemFunctionImplementor);
map.put(CURRENT_DATE, systemFunctionImplementor);
map.put(CURRENT_DATETIME, systemFunctionImplementor);
map.put(LOCALTIME, systemFunctionImplementor);
map.put(LOCALTIMESTAMP, systemFunctionImplementor);

Expand Down Expand Up @@ -3482,6 +3484,13 @@ private static class SystemFunctionImplementor
return Expressions.call(BuiltInMethod.CURRENT_TIME.method, root);
} else if (op == CURRENT_DATE) {
return Expressions.call(BuiltInMethod.CURRENT_DATE.method, root);
} else if (op == CURRENT_DATETIME) {
if (call.getOperands().isEmpty()) {
return Expressions.call(BuiltInMethod.CURRENT_DATETIME.method, root);
} else {
return Expressions.call(BuiltInMethod.CURRENT_DATETIME2.method, root,
argValueList.get(0));
}
} else if (op == LOCALTIMESTAMP) {
return Expressions.call(BuiltInMethod.LOCAL_TIMESTAMP.method, root);
} else if (op == LOCALTIME) {
Expand Down
17 changes: 17 additions & 0 deletions core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
Original file line number Diff line number Diff line change
Expand Up @@ -4708,6 +4708,23 @@ public static int currentDate(DataContext root) {
return date;
}

/** SQL {@code CURRENT_DATETIME} function. */
@NonDeterministic
public static Long currentDatetime(DataContext root) {
final long timestamp = DataContext.Variable.CURRENT_TIMESTAMP.get(root);
return datetime(timestamp);
}

/** SQL {@code CURRENT_DATETIME} function with a specified timezone. */
@NonDeterministic
public static @Nullable Long currentDatetime(DataContext root, @Nullable String timezone) {
if (timezone == null) {
return null;
}
final long timestamp = DataContext.Variable.UTC_TIMESTAMP.get(root);
return datetime(timestamp, timezone);
}

/** SQL {@code LOCAL_TIMESTAMP} function. */
@NonDeterministic
public static long localTimestamp(DataContext root) {
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/apache/calcite/sql/SqlSyntax.java
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public enum SqlSyntax {
*
* @see SqlConformance#allowNiladicParentheses()
*/
FUNCTION_ID {
FUNCTION_ID(FUNCTION) {
@Override public void unparse(
SqlWriter writer,
SqlOperator operator,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -801,9 +801,10 @@ static RelDataType deriveTypeSplit(SqlOperatorBinding operatorBinding,
@LibraryOperator(libraries = {BIG_QUERY})
public static final SqlFunction CURRENT_DATETIME =
SqlBasicFunction.create("CURRENT_DATETIME",
ReturnTypes.TIMESTAMP.andThen(SqlTypeTransforms.TO_NULLABLE),
ReturnTypes.TIMESTAMP_NULLABLE,
OperandTypes.NILADIC.or(OperandTypes.STRING),
SqlFunctionCategory.TIMEDATE);
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. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,24 @@ public void add(SqlOperator op) {
SqlSyntax syntax,
List<SqlOperator> operatorList,
SqlNameMatcher nameMatcher) {
for (SqlOperator operator : this.operatorList) {
if (operator.getSyntax().family != syntax) {
continue;
}
for (SqlOperator op : this.operatorList) {
if (!opName.isSimple()
|| !nameMatcher.matches(operator.getName(), opName.getSimple())) {
|| !nameMatcher.matches(op.getName(), opName.getSimple())) {
continue;
}
if (category != null
&& category != category(operator)
&& category != category(op)
&& !category.isUserDefinedNotSpecificFunction()) {
continue;
}
operatorList.add(operator);
if (op.getSyntax() == syntax) {
operatorList.add(op);
} else if (syntax == SqlSyntax.FUNCTION
&& op instanceof SqlFunction) {
// this special case is needed for operators like CAST,
// which are treated as functions but have special syntax
operatorList.add(op);
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,9 @@ public enum BuiltInMethod {
CURRENT_TIMESTAMP(SqlFunctions.class, "currentTimestamp", DataContext.class),
CURRENT_TIME(SqlFunctions.class, "currentTime", DataContext.class),
CURRENT_DATE(SqlFunctions.class, "currentDate", DataContext.class),
CURRENT_DATETIME(SqlFunctions.class, "currentDatetime", DataContext.class),
CURRENT_DATETIME2(SqlFunctions.class, "currentDatetime", DataContext.class,
String.class),
LOCAL_TIMESTAMP(SqlFunctions.class, "localTimestamp", DataContext.class),
LOCAL_TIME(SqlFunctions.class, "localTime", DataContext.class),
TIME_ZONE(SqlFunctions.class, "timeZone", DataContext.class),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1047,7 +1047,7 @@ void testAggregateWithAlias(SqlExplainFormat format) {
String relJson = jsonWriter.asString();
String result = deserializeAndDumpToTextFormat(getSchema(rel), relJson);
final String expected = ""
+ "LogicalProject(JOB=[$2], $f1=[CURRENT_DATETIME()])\n"
+ "LogicalProject(JOB=[$2], $f1=[CURRENT_DATETIME])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
assertThat(result, isLinux(expected));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1615,6 +1615,9 @@ void testLikeAndSimilarFails() {
shouldFail.fails("Column 'CURRENT_DATETIME' not found in any table");

final SqlOperatorTable opTable = operatorTableFor(SqlLibrary.BIG_QUERY);
sql("select current_datetime")
.withConformance(SqlConformanceEnum.BIG_QUERY)
.withOperatorTable(opTable).ok();
sql("select current_datetime()")
.withConformance(SqlConformanceEnum.BIG_QUERY)
.withOperatorTable(opTable).ok();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6986,9 +6986,45 @@ private static void checkIf(SqlOperatorFixture f) {
// allow it as a function.
f.checkNull("DATE(null)");
f.checkScalar("DATE('1985-12-06')", "1985-12-06", "DATE NOT NULL");
}

/** Tests that the {@code CURRENT_DATETIME} function is defined in the
* BigQuery library, is not available in the default library,
* and can be called with and without parentheses. */
@Test void testCurrentDatetimeFunc() {
SqlOperatorFixture f0 = fixture()
.setFor(SqlLibraryOperators.CURRENT_DATETIME);

// In default conformance, with BigQuery operator table,
// CURRENT_DATETIME is valid only without parentheses.
final SqlOperatorFixture f1 =
f0.withLibrary(SqlLibrary.BIG_QUERY);
f1.checkType("CURRENT_DATETIME", "TIMESTAMP(0) NOT NULL");
f1.checkFails("^CURRENT_DATETIME()^",
"No match found for function signature CURRENT_DATETIME\\(\\)",
false);

// In BigQuery conformance, with BigQuery operator table,
// CURRENT_DATETIME should be valid with and without parentheses.
// We cannot execute it because results are non-deterministic.
SqlOperatorFixture f =
f1.withConformance(SqlConformanceEnum.BIG_QUERY);
f.checkType("CURRENT_DATETIME", "TIMESTAMP(0) NOT NULL");
f.checkType("CURRENT_DATETIME()", "TIMESTAMP(0) NOT NULL");
f.checkType("CURRENT_DATETIME('America/Los_Angeles')", "TIMESTAMP(0) NOT NULL");
f.checkType("CURRENT_DATETIME('America/Los_Angeles')",
"TIMESTAMP(0) NOT NULL");
f.checkType("CURRENT_DATETIME(CAST(NULL AS VARCHAR(20)))", "TIMESTAMP(0)");
f.checkNull("CURRENT_DATETIME(CAST(NULL AS VARCHAR(20)))");

// In BigQuery conformance, but with the default operator table,
// CURRENT_DATETIME is not found.
final SqlOperatorFixture f2 =
f0.withConformance(SqlConformanceEnum.BIG_QUERY);
f2.checkFails("^CURRENT_DATETIME^",
"Column 'CURRENT_DATETIME' not found in any table", false);
f2.checkFails("^CURRENT_DATETIME()^",
"No match found for function signature CURRENT_DATETIME\\(\\)",
false);
}

@Test void testAbsFunc() {
Expand Down

0 comments on commit db60219

Please sign in to comment.