This is an automated email from the ASF dual-hosted git repository.
jhyde 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 1156efba97 [CALCITE-5360] Add TIMESTAMP_ADD function (enabled in
BigQuery library)
1156efba97 is described below
commit 1156efba972c90ab7a484e745d95bcecadf27ecc
Author: Tanner Clary <[email protected]>
AuthorDate: Mon Dec 5 20:11:02 2022 +0000
[CALCITE-5360] Add TIMESTAMP_ADD function (enabled in BigQuery library)
The JDBC and builtin functions already contain a TIMESTAMPADD
function with three arguments; this change adds a
TIMESTAMP_ADD function with two arguments, consistent with
BigQuery, and enabled if you have 'lib=bigquery' in your
connection options.
Close apache/calcite#2998
---
babel/src/test/resources/sql/big-query.iq | 18 +++++++++
.../calcite/sql/fun/SqlLibraryOperators.java | 14 +++++++
.../calcite/sql2rel/StandardConvertletTable.java | 28 +++++++++++---
.../src/main/java/org/apache/calcite/util/Bug.java | 15 ++++----
site/_docs/reference.md | 1 +
.../org/apache/calcite/test/SqlOperatorTest.java | 43 ++++++++++++++++++++++
6 files changed, 106 insertions(+), 13 deletions(-)
diff --git a/babel/src/test/resources/sql/big-query.iq
b/babel/src/test/resources/sql/big-query.iq
index 61c07698ae..7795f54a2b 100755
--- a/babel/src/test/resources/sql/big-query.iq
+++ b/babel/src/test/resources/sql/big-query.iq
@@ -1197,6 +1197,8 @@ SELECT
# Display of results may differ, depending upon the environment and
# time zone where this query was executed.
!if (false) {
+# Enable when we have TIMESTAMP() function,
+# and when we allow "+00" timezone offset in timestamp values.
SELECT
TIMESTAMP("2008-12-25 15:30:00+00") AS original,
TIMESTAMP_ADD(TIMESTAMP "2008-12-25 15:30:00+00", INTERVAL 10 MINUTE) AS
later;
@@ -1205,9 +1207,25 @@ SELECT
+-------------------------+-------------------------+
| 2008-12-25 15:30:00 UTC | 2008-12-25 15:40:00 UTC |
+-------------------------+-------------------------+
+(1 row)
+
!ok
!}
+# As previous, but uses TIMESTAMP literal rather than function,
+# and "+00" timezone offsets have been removed.
+SELECT
+ TIMESTAMP "2008-12-25 15:30:00" AS original,
+ TIMESTAMP_ADD(TIMESTAMP "2008-12-25 15:30:00", INTERVAL 10 MINUTE) AS later;
++---------------------+---------------------+
+| original | later |
++---------------------+---------------------+
+| 2008-12-25 15:30:00 | 2008-12-25 15:40:00 |
++---------------------+---------------------+
+(1 row)
+
+!ok
+
#####################################################################
# DATE_SUB
#
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 7566f2331a..f8ea6c7821 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
@@ -634,6 +634,20 @@ public abstract class SqlLibraryOperators {
ImmutableSet.<TimeUnitRange>builder()
.addAll(MONTH_UNITS).addAll(DATE_UNITS).addAll(TIME_UNITS).build();
+ /** The "TIMESTAMP_ADD(timestamp_expression, interval_expression)" function
+ * (BigQuery), the two-argument variant of the built-in
+ * {@link SqlStdOperatorTable#TIMESTAMP_ADD TIMESTAMPADD} function, which has
+ * three arguments.
+ *
+ * <p>In BigQuery, the syntax is "TIMESTAMP_ADD(timestamp_expression,
INTERVAL
+ * int64_expression date_part)" but in Calcite the second argument can be any
+ * interval expression, not just an interval literal. */
+ @LibraryOperator(libraries = {BIG_QUERY})
+ public static final SqlFunction TIMESTAMP_ADD2 =
+ SqlBasicFunction.create(SqlKind.TIMESTAMP_ADD, ReturnTypes.ARG0_NULLABLE,
+ OperandTypes.TIMESTAMP_INTERVAL)
+ .withFunctionType(SqlFunctionCategory.TIMEDATE);
+
/** The "TIME_TRUNC(time_expression, time_part)" function (BigQuery);
* truncates a TIME value to the granularity of time_part. The TIME value is
* always rounded to the beginning of time_part. */
diff --git
a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
index 3b5eda2869..727ea53910 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
@@ -165,6 +165,9 @@ public class StandardConvertletTable extends
ReflectiveConvertletTable {
registerOp(SqlLibraryOperators.SUBSTR_POSTGRESQL,
new SubstrConvertlet(SqlLibrary.POSTGRESQL));
+ registerOp(SqlLibraryOperators.TIMESTAMP_ADD2,
+ new TimestampAddConvertlet());
+
registerOp(SqlLibraryOperators.NVL, StandardConvertletTable::convertNvl);
registerOp(SqlLibraryOperators.DECODE,
StandardConvertletTable::convertDecode);
@@ -261,7 +264,6 @@ public class StandardConvertletTable extends
ReflectiveConvertletTable {
final SqlRexConvertlet floorCeilConvertlet = new FloorCeilConvertlet();
registerOp(SqlStdOperatorTable.FLOOR, floorCeilConvertlet);
registerOp(SqlStdOperatorTable.CEIL, floorCeilConvertlet);
-
registerOp(SqlStdOperatorTable.TIMESTAMP_ADD, new
TimestampAddConvertlet());
registerOp(SqlStdOperatorTable.TIMESTAMP_DIFF,
new TimestampDiffConvertlet());
@@ -1822,18 +1824,34 @@ public class StandardConvertletTable extends
ReflectiveConvertletTable {
}
}
- /** Convertlet that handles the {@code TIMESTAMPADD} function. */
+ /** Convertlet that handles the 3-argument {@code TIMESTAMPADD} function
+ * and the 2-argument BigQuery-style {@code TIMESTAMP_ADD} function. */
private static class TimestampAddConvertlet implements SqlRexConvertlet {
@Override public RexNode convertCall(SqlRexContext cx, SqlCall call) {
// TIMESTAMPADD(unit, count, timestamp)
// => timestamp + count * INTERVAL '1' UNIT
final RexBuilder rexBuilder = cx.getRexBuilder();
- SqlIntervalQualifier qualifier = call.operand(0);
+ SqlIntervalQualifier qualifier;
+ final RexNode op1;
+ final RexNode op2;
+ switch (call.operandCount()) {
+ case 2:
+ // BigQuery-style 'TIMESTAMP_ADD(timestamp, interval)'
+ final SqlBasicCall operandCall = call.operand(1);
+ qualifier = operandCall.operand(1);
+ op1 = cx.convertExpression(operandCall.operand(0));
+ op2 = cx.convertExpression(call.operand(0));
+ break;
+ default:
+ // JDBC-style 'TIMESTAMPADD(unit, count, timestamp)'
+ qualifier = call.operand(0);
+ op1 = cx.convertExpression(call.operand(1));
+ op2 = cx.convertExpression(call.operand(2));
+ }
+
final TimeFrame timeFrame =
cx.getValidator().validateTimeFrame(qualifier);
final TimeUnit unit = first(timeFrame.unit(), TimeUnit.EPOCH);
final RelDataType type = cx.getValidator().getValidatedNodeType(call);
- final RexNode op1 = cx.convertExpression(call.operand(1));
- final RexNode op2 = cx.convertExpression(call.operand(2));
if (unit == TimeUnit.EPOCH && qualifier.timeFrameName != null) {
// Custom time frames have a different path. They are kept as names,
// and then handled by Java functions such as
diff --git a/core/src/main/java/org/apache/calcite/util/Bug.java
b/core/src/main/java/org/apache/calcite/util/Bug.java
index 602984c5cf..09e1cd5832 100644
--- a/core/src/main/java/org/apache/calcite/util/Bug.java
+++ b/core/src/main/java/org/apache/calcite/util/Bug.java
@@ -152,14 +152,6 @@ public abstract class Bug {
* Decorrelate sub-queries in Project and Join</a> is fixed. */
public static final boolean CALCITE_1045_FIXED = false;
- /**
- * Whether
- * <a
href="https://issues.apache.org/jira/browse/CALCITE-2223">[CALCITE-2223]
- * ProjectMergeRule is infinitely matched when is applied after
ProjectReduceExpressions Rule</a>
- * is fixed.
- */
- public static final boolean CALCITE_2223_FIXED = false;
-
/** Whether
* <a
href="https://issues.apache.org/jira/browse/CALCITE-2400">[CALCITE-2400]
* Allow standards-compliant column ordering for NATURAL JOIN and JOIN USING
@@ -191,6 +183,7 @@ public abstract class Bug {
* Intermittent precision in Druid results when using aggregation functions
over columns of type
* DOUBLE</a> is fixed. */
public static final boolean CALCITE_4204_FIXED = false;
+
/** Whether
* <a
href="https://issues.apache.org/jira/browse/CALCITE-4205">[CALCITE-4205]
* DruidAdapterIT#testDruidTimeFloorAndTimeParseExpressions2 fails</a> is
fixed. */
@@ -200,12 +193,18 @@ public abstract class Bug {
* Druid plans with small intervals should be chosen over full interval scan
plus filter</a> is
* fixed. */
public static final boolean CALCITE_4213_FIXED = false;
+
/** Whether
* <a
href="https://issues.apache.org/jira/browse/CALCITE-4645">[CALCITE-4645]
* In Elasticsearch adapter, a range predicate should be translated to a
range query</a> is
* fixed. */
public static final boolean CALCITE_4645_FIXED = false;
+ /** Whether
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-5422">[CALCITE-5422]
+ * MILLISECOND and MICROSECOND units in INTERVAL literal</a> is fixed. */
+ public static final boolean CALCITE_5422_FIXED = false;
+
/**
* Use this to flag temporary code.
*/
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index 74724ef866..c81ed03159 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -2653,6 +2653,7 @@ semantics.
| b m o p | 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
| 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 o | TANH(numeric) | Returns the hyperbolic
tangent of *numeric*
+| b | TIMESTAMP_ADD(timestamp, interval int64 date_part) | Adds
int64_expression units of date_part to the timestamp, independent of any time
zone.
| b | TIMESTAMP_MICROS(integer) | Returns the TIMESTAMP
that is *integer* microseconds after 1970-01-01 00:00:00
| b | TIMESTAMP_MILLIS(integer) | Returns the TIMESTAMP
that is *integer* milliseconds after 1970-01-01 00:00:00
| b | TIMESTAMP_SECONDS(integer) | Returns the TIMESTAMP
that is *integer* seconds after 1970-01-01 00:00:00
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 af13da2828..ec03be71e3 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -7725,6 +7725,49 @@ public class SqlOperatorTest {
"TIMESTAMP(3) NOT NULL");
}
+ /** Tests {@code TIMESTAMP_ADD}, BigQuery's 2-argument variant of the
+ * 3-argument {@code TIMESTAMPADD} function. */
+ @Test void testTimestampAdd2() {
+ final SqlOperatorFixture f0 = fixture()
+ .setFor(SqlLibraryOperators.TIMESTAMP_ADD2);
+ f0.checkFails("^timestamp_add(timestamp '2008-12-25 15:30:00', "
+ + "interval 5 minute)^",
+ "No match found for function signature "
+ + "TIMESTAMP_ADD\\(<TIMESTAMP>, <INTERVAL_DAY_TIME>\\)", false);
+
+ final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.BIG_QUERY);
+ if (Bug.CALCITE_5422_FIXED) {
+ f.checkScalar("timestamp_add(timestamp '2008-12-25 15:30:00', "
+ + "interval 100000000000 microsecond)",
+ "2008-12-26 19:16:40",
+ "TIMESTAMP(3) NOT NULL");
+ f.checkScalar("timestamp_add(timestamp '2008-12-25 15:30:00', "
+ + "interval 100000000 millisecond)",
+ "2008-12-26 19:16:40",
+ "TIMESTAMP(3) NOT NULL");
+ }
+
+ f.checkScalar("timestamp_add(timestamp '2016-02-24 12:42:25', interval 2
second)",
+ "2016-02-24 12:42:27",
+ "TIMESTAMP(0) NOT NULL");
+ f.checkScalar("timestamp_add(timestamp '2016-02-24 12:42:25', interval 2
minute)",
+ "2016-02-24 12:44:25",
+ "TIMESTAMP(0) NOT NULL");
+ f.checkScalar("timestamp_add(timestamp '2016-02-24 12:42:25', interval
-2000 hour)",
+ "2015-12-03 04:42:25",
+ "TIMESTAMP(0) NOT NULL");
+ f.checkScalar("timestamp_add(timestamp '2016-02-24 12:42:25', interval 1
day)",
+ "2016-02-25 12:42:25",
+ "TIMESTAMP(0) NOT NULL");
+ f.checkScalar("timestamp_add(timestamp '2016-02-24 12:42:25', interval 1
month)",
+ "2016-03-24 12:42:25",
+ "TIMESTAMP(0) NOT NULL");
+ f.checkScalar("timestamp_add(timestamp '2016-02-24 12:42:25', interval 1
year)",
+ "2017-02-24 12:42:25",
+ "TIMESTAMP(0) NOT NULL");
+ f.checkNull("timestamp_add(CAST(NULL AS TIMESTAMP), interval 5 minute)");
+ }
+
@Test void testTimestampDiff() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.TIMESTAMP_DIFF, VmName.EXPAND);