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 659ea980b0 [CALCITE-6612] Add DATE_SUB function(enabled in Spark 
library)
659ea980b0 is described below

commit 659ea980b077ed6721debc27f19eedaa9616e0ab
Author: Cancai Cai <[email protected]>
AuthorDate: Mon Sep 30 18:06:37 2024 +0800

    [CALCITE-6612] Add DATE_SUB function(enabled in Spark library)
---
 .../calcite/sql/fun/SqlLibraryOperators.java       | 10 ++++++-
 .../calcite/sql2rel/StandardConvertletTable.java   | 20 ++++++++++---
 site/_docs/reference.md                            |  1 +
 .../org/apache/calcite/test/SqlOperatorTest.java   | 34 ++++++++++++++++++++++
 4 files changed, 60 insertions(+), 5 deletions(-)

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 306630e6cc..edde88f758 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
@@ -132,7 +132,7 @@ public abstract class SqlLibraryOperators {
   public static final SqlFunction DATEADD =
       new SqlTimestampAddFunction("DATEADD");
 
-  /** The "DATE_ADD(start_date, num_days)" function
+  /** The "DATE_ADD(date, numDays)" function
    * (Spark) Returns the date that is num_days after start_date. */
   @LibraryOperator(libraries = {SPARK})
   public static final SqlFunction DATE_ADD_SPARK =
@@ -140,6 +140,14 @@ public abstract class SqlLibraryOperators {
               OperandTypes.DATE_ANY)
           .withFunctionType(SqlFunctionCategory.TIMEDATE);
 
+  /** The "DATE_SUB(date, numDays)" function
+   * (Spark) Returns the date that is num_days before start_date.*/
+  @LibraryOperator(libraries = {SPARK})
+  public static final SqlFunction DATE_SUB_SPARK =
+      SqlBasicFunction.create(SqlKind.DATE_SUB, ReturnTypes.DATE_NULLABLE,
+              OperandTypes.DATE_ANY)
+          .withFunctionType(SqlFunctionCategory.TIMEDATE);
+
   /** The "ADD_MONTHS(start_date, num_months)" function
    * (SPARK) Returns the date that is num_months after start_date. */
   @LibraryOperator(libraries = {ORACLE, SPARK})
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 3428c4cea3..7d9c807cd0 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
@@ -216,6 +216,8 @@ public class StandardConvertletTable extends 
ReflectiveConvertletTable {
         new TimestampDiffConvertlet());
     registerOp(SqlLibraryOperators.DATE_SUB,
         new TimestampSubConvertlet());
+    registerOp(SqlLibraryOperators.DATE_SUB_SPARK,
+        new TimestampSubConvertlet());
     registerOp(SqlLibraryOperators.DATETIME_ADD,
         new TimestampAddConvertlet());
     registerOp(SqlLibraryOperators.DATETIME_DIFF,
@@ -2309,10 +2311,20 @@ public class StandardConvertletTable extends 
ReflectiveConvertletTable {
       //  => timestamp - count * INTERVAL '1' UNIT
       final RexBuilder rexBuilder = cx.getRexBuilder();
       final SqlParserPos pos = call.getParserPosition();
-      final SqlBasicCall operandCall = call.operand(1);
-      SqlIntervalQualifier qualifier = operandCall.operand(1);
-      final RexNode op1 = cx.convertExpression(operandCall.operand(0));
-      final RexNode op2 = cx.convertExpression(call.operand(0));
+      SqlIntervalQualifier qualifier;
+      final RexNode op1;
+      final RexNode op2;
+      if (call.getOperator() == SqlLibraryOperators.DATE_SUB_SPARK) {
+        // Spark-style 'DATE_SUB(date, integer days)'
+        qualifier = new SqlIntervalQualifier(TimeUnit.DAY, null, 
SqlParserPos.ZERO);
+        op2 = handleFirstParameter(cx, rexBuilder, call);
+        op1 = handleSecondParameter(cx, rexBuilder, call);
+      } else {
+        final SqlBasicCall operandCall = call.operand(1);
+        qualifier = operandCall.operand(1);
+        op1 = cx.convertExpression(operandCall.operand(0));
+        op2 = cx.convertExpression(call.operand(0));
+      }
       final TimeFrame timeFrame = 
cx.getValidator().validateTimeFrame(qualifier);
       final TimeUnit unit = first(timeFrame.unit(), TimeUnit.EPOCH);
       final RexNode interval2Sub;
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index c18bf6f691..9924940c59 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -2808,6 +2808,7 @@ In the following:
 | s | DATE_ADD(date, numDays)                        | Returns the DATE that 
is *numDays* after *date*
 | b | DATE_DIFF(date, date2, timeUnit)               | Returns the whole 
number of *timeUnit* between *date* and *date2*
 | b | DATE_SUB(date, interval)                       | Returns the DATE value 
that occurs *interval* before *date*
+| s | DATE_SUB(date, numDays)                        | Returns the DATE that 
is *numDays* before *date*
 | b | DATE_TRUNC(date, timeUnit)                     | Truncates *date* to the 
granularity of *timeUnit*, rounding to the beginning of the unit
 | o r s | DECODE(value, value1, result1 [, valueN, resultN ]* [, default ]) | 
Compares *value* to each *valueN* value one by one; if *value* is equal to a 
*valueN*, returns the corresponding *resultN*, else returns *default*, or NULL 
if *default* is not specified
 | p r | DIFFERENCE(string, string)                   | Returns a measure of 
the similarity of two strings, namely the number of character positions that 
their `SOUNDEX` values have in common: 4 if the `SOUNDEX` values are same and 0 
if the `SOUNDEX` values are totally different
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 5a4c7ecc3b..5ba92aa601 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -14060,6 +14060,40 @@ public class SqlOperatorTest {
         false);
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6612";>[CALCITE-6612]
+   * Add DATE_SUB function (enabled in Spark library)</a>.
+   */
+  @Test void testDateSubSpark() {
+    final SqlOperatorFixture f0 = fixture()
+        .setFor(SqlLibraryOperators.DATE_SUB_SPARK);
+    f0.checkFails("^date_sub(date '2008-12-25', "
+            + "5)^",
+        "No match found for function signature "
+            + "DATE_SUB\\(<DATE>, <NUMERIC>\\)", false);
+
+    final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.SPARK);
+    f.checkScalar("date_sub(date '2016-02-22', 2)",
+        "2016-02-20",
+        "DATE NOT NULL");
+    f.checkScalar("date_sub(date '2016-03-01', 2)",
+        "2016-02-28",
+        "DATE NOT NULL");
+    f.checkScalar("date_sub(timestamp '2016-02-22 13:00:01', '-2.0')",
+        "2016-02-24",
+        "DATE NOT NULL");
+    f.checkScalar("date_sub(timestamp '2016-02-22 13:00:01', -2)",
+        "2016-02-24",
+        "DATE NOT NULL");
+    f.checkNull("date_sub(CAST(NULL AS DATE), 5)");
+    f.checkNull("date_sub(date '2016-02-22', CAST(NULL AS INTEGER))");
+    f.checkNull("date_sub(CAST(NULL AS DATE), CAST(NULL AS INTEGER))");
+    f.checkFails("^date_sub(time '13:00:01', -2)^", 
INVALID_ARGUMENTS_TYPE_VALIDATION_ERROR,
+        false);
+    f.checkFails("^date_sub(1, -2)^", INVALID_ARGUMENTS_TYPE_VALIDATION_ERROR,
+        false);
+  }
+
   /** Test case for
    * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6396";>[CALCITE-6396]
    * Add ADD_MONTHS function (enabled in Oracle, Spark library)</a>.

Reply via email to