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

yashmayya pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new 95c071c0b95 Fix MSE constant folding of year-month INTERVAL arithmetic 
(#19788)
95c071c0b95 is described below

commit 95c071c0b95d0ca2a7f725d961a4095193d1e709
Author: Yash Mayya <[email protected]>
AuthorDate: Thu Oct 15 14:45:16 2026 -0700

    Fix MSE constant folding of year-month INTERVAL arithmetic (#19788)
---
 .../rel/rules/PinotEvaluateLiteralRule.java        | 131 ++++++++++
 .../resources/queries/LiteralEvaluationPlans.json  | 265 +++++++++++++++++++++
 2 files changed, 396 insertions(+)

diff --git 
a/pinot-query-planner/src/main/java/org/apache/pinot/calcite/rel/rules/PinotEvaluateLiteralRule.java
 
b/pinot-query-planner/src/main/java/org/apache/pinot/calcite/rel/rules/PinotEvaluateLiteralRule.java
index 1c721f203ae..856b776c966 100644
--- 
a/pinot-query-planner/src/main/java/org/apache/pinot/calcite/rel/rules/PinotEvaluateLiteralRule.java
+++ 
b/pinot-query-planner/src/main/java/org/apache/pinot/calcite/rel/rules/PinotEvaluateLiteralRule.java
@@ -21,14 +21,18 @@ package org.apache.pinot.calcite.rel.rules;
 import com.google.common.base.Preconditions;
 import java.lang.reflect.Array;
 import java.math.BigDecimal;
+import java.math.MathContext;
+import java.math.RoundingMode;
 import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Calendar;
 import java.util.GregorianCalendar;
 import java.util.List;
 import java.util.Objects;
 import javax.annotation.Nullable;
 import org.apache.calcite.avatica.util.ByteString;
+import org.apache.calcite.avatica.util.DateTimeUtils;
 import org.apache.calcite.avatica.util.TimeUnitRange;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
@@ -43,11 +47,14 @@ import org.apache.calcite.rex.RexShuttle;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.type.ArraySqlType;
 import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.type.SqlTypeUtil;
 import org.apache.calcite.tools.RelBuilderFactory;
+import org.apache.calcite.util.DateString;
 import org.apache.calcite.util.NlsString;
 import org.apache.pinot.common.function.FunctionInfo;
 import org.apache.pinot.common.function.FunctionRegistry;
 import org.apache.pinot.common.function.QueryFunctionInvoker;
+import org.apache.pinot.common.function.scalar.DateTimeFunctions;
 import org.apache.pinot.common.utils.DataSchema.ColumnDataType;
 import org.apache.pinot.query.planner.logical.RelToPlanNodeConverter;
 import org.apache.pinot.spi.utils.TimestampUtils;
@@ -171,6 +178,12 @@ public class PinotEvaluateLiteralRule {
       arguments[i] = getLiteralValue(rexLiteral);
     }
 
+    if (isDatetimeIntervalArithmetic(rexCall)) {
+      return evaluateDatetimeIntervalArithmetic(rexCall, rexBuilder);
+    }
+    if (isIntervalArithmetic(rexCall)) {
+      return evaluateIntervalArithmetic(rexCall, rexBuilder);
+    }
     if (rexCall.getKind() == SqlKind.CAST) {
       // Handle separately because the CAST operator only has one operand (the 
value to be cast) and the type to be cast
       // to is determined by the operator's return type. Pinot's CAST function 
implementation requires two arguments:
@@ -248,6 +261,114 @@ public class PinotEvaluateLiteralRule {
     }
   }
 
+  /// Returns whether the call is `datetime + interval` (Calcite's 
`DATETIME_PLUS`) or `datetime - interval`
+  /// (`MINUS_DATE`) with a `TIMESTAMP` or `DATE` result. Calcite puts the 
interval second for both.
+  private static boolean isDatetimeIntervalArithmetic(RexCall rexCall) {
+    SqlKind kind = rexCall.getKind();
+    SqlTypeName resultType = rexCall.getType().getSqlTypeName();
+    List<RexNode> operands = rexCall.getOperands();
+    return (kind == SqlKind.PLUS || kind == SqlKind.MINUS) && (resultType == 
SqlTypeName.TIMESTAMP
+        || resultType == SqlTypeName.DATE) && operands.size() == 2 && 
SqlTypeUtil.isInterval(operands.get(1).getType());
+  }
+
+  /// Evaluates `datetime + interval` or `datetime - interval` with the same 
semantics as Calcite. It cannot use Pinot's
+  /// `plus` / `minus` functions, because Calcite stores a day-time interval 
as milliseconds but a year-month interval
+  /// as months: they would add `INTERVAL '1' MONTH` as 1 millisecond. Months 
are added with the same calendar
+  /// arithmetic as `TIMESTAMPADD(MONTH, ...)`, so `2019-01-31 + INTERVAL '1' 
MONTH` is `2019-02-28`. Like Calcite, only
+  /// the whole days of a day-time interval are added to a `DATE`.
+  private static RexNode evaluateDatetimeIntervalArithmetic(RexCall rexCall, 
RexBuilder rexBuilder) {
+    RexLiteral datetime = (RexLiteral) rexCall.getOperands().get(0);
+    RexLiteral interval = (RexLiteral) rexCall.getOperands().get(1);
+    RelDataType resultType = rexCall.getType();
+    if (datetime.isNull() || interval.isNull()) {
+      return rexBuilder.makeNullLiteral(resultType);
+    }
+    Object resultValue;
+    try {
+      long millis = 
Objects.requireNonNull(datetime.getValueAs(Calendar.class)).getTimeInMillis();
+      // Number of months for a year-month interval, number of milliseconds 
for a day-time interval
+      long intervalValue = 
Objects.requireNonNull(interval.getValueAs(BigDecimal.class)).longValueExact();
+      if (rexCall.getKind() == SqlKind.MINUS) {
+        intervalValue = Math.negateExact(intervalValue);
+      }
+      boolean isDate = resultType.getSqlTypeName() == SqlTypeName.DATE;
+      long resultMillis;
+      if 
(SqlTypeName.YEAR_INTERVAL_TYPES.contains(interval.getType().getSqlTypeName())) 
{
+        resultMillis = DateTimeFunctions.timestampAdd("MONTH", intervalValue, 
millis);
+      } else {
+        if (isDate) {
+          intervalValue = intervalValue / DateTimeUtils.MILLIS_PER_DAY * 
DateTimeUtils.MILLIS_PER_DAY;
+        }
+        resultMillis = Math.addExact(millis, intervalValue);
+      }
+      resultValue = isDate ? DateString.fromDaysSinceEpoch(
+          Math.toIntExact(Math.floorDiv(resultMillis, 
DateTimeUtils.MILLIS_PER_DAY))) : resultMillis;
+    } catch (Exception e) {
+      throw new SqlCompilationException(
+          "Caught exception while evaluating: " + rexCall + ": " + 
e.getMessage(), e);
+    }
+    return rexBuilder.makeLiteral(resultValue, resultType, false);
+  }
+
+  /// Returns whether the call is `+`, `-`, `*` or `/` with an interval result 
and only interval or numeric operands,
+  /// e.g. `INTERVAL '1' MONTH * 2`.
+  private static boolean isIntervalArithmetic(RexCall rexCall) {
+    SqlKind kind = rexCall.getKind();
+    List<RexNode> operands = rexCall.getOperands();
+    return (kind == SqlKind.PLUS || kind == SqlKind.MINUS || kind == 
SqlKind.TIMES || kind == SqlKind.DIVIDE)
+        && SqlTypeUtil.isInterval(rexCall.getType()) && operands.size() == 2 
&& operands.stream()
+        .allMatch(operand -> SqlTypeUtil.isInterval(operand.getType()) || 
SqlTypeUtil.isNumeric(operand.getType()));
+  }
+
+  /// Evaluates interval arithmetic exactly. Pinot's `times` / `divide` 
functions compute in DOUBLE, so
+  /// `INTERVAL '100' MONTH * 0.29` would be 28.999999999999996 months instead 
of 29, and `INTERVAL '1' DAY * 0.7` would
+  /// be 1 millisecond short.
+  private static RexNode evaluateIntervalArithmetic(RexCall rexCall, 
RexBuilder rexBuilder) {
+    RexLiteral left = (RexLiteral) rexCall.getOperands().get(0);
+    RexLiteral right = (RexLiteral) rexCall.getOperands().get(1);
+    RelDataType resultType = rexCall.getType();
+    if (left.isNull() || right.isNull()) {
+      return rexBuilder.makeNullLiteral(resultType);
+    }
+    BigDecimal resultValue;
+    try {
+      // An interval literal holds its number of months or milliseconds
+      BigDecimal leftValue = 
Objects.requireNonNull(left.getValueAs(BigDecimal.class));
+      BigDecimal rightValue = 
Objects.requireNonNull(right.getValueAs(BigDecimal.class));
+      switch (rexCall.getKind()) {
+        case PLUS:
+          resultValue = leftValue.add(rightValue);
+          break;
+        case MINUS:
+          resultValue = leftValue.subtract(rightValue);
+          break;
+        case TIMES:
+          resultValue = leftValue.multiply(rightValue);
+          break;
+        default:
+          resultValue = leftValue.divide(rightValue, MathContext.DECIMAL128);
+          break;
+      }
+      resultValue = toIntervalValue(resultValue, resultType);
+    } catch (Exception e) {
+      throw new SqlCompilationException(
+          "Caught exception while evaluating: " + rexCall + ": " + 
e.getMessage(), e);
+    }
+    return rexBuilder.makeLiteral(resultValue, resultType, false);
+  }
+
+  /// Converts a number to the value that Calcite stores for an interval: a 
whole number of months for a year-month
+  /// interval, and milliseconds for a day-time interval (like Calcite, 
fractions of a millisecond are dropped).
+  private static BigDecimal toIntervalValue(BigDecimal value, RelDataType 
intervalType) {
+    if 
(SqlTypeName.YEAR_INTERVAL_TYPES.contains(intervalType.getSqlTypeName())) {
+      // Intentionally stricter than Calcite, which truncates to whole months 
(INTERVAL '1' MONTH / 2 is 0 months):
+      // reject the query instead of silently dropping a fraction of a month
+      Preconditions.checkArgument(value.stripTrailingZeros().scale() <= 0,
+          "Year-month interval must be a whole number of months, got: %s", 
value);
+    }
+    return value.setScale(0, RoundingMode.DOWN);
+  }
+
   private static RelDataType convertDecimalType(RelDataType relDataType, 
RexBuilder rexBuilder) {
     Preconditions.checkArgument(relDataType.getSqlTypeName() == 
SqlTypeName.DECIMAL);
     return 
RelToPlanNodeConverter.convertToColumnDataType(relDataType).toType(rexBuilder.getTypeFactory());
@@ -307,6 +428,16 @@ public class PinotEvaluateLiteralRule {
     if (relDataType.getSqlTypeName() == SqlTypeName.DECIMAL) {
       return new BigDecimal(resultValue.toString());
     }
+    // Other functions with an interval result, e.g. ABS(INTERVAL '-1' MONTH). 
Interval arithmetic is evaluated exactly
+    // in evaluateIntervalArithmetic().
+    if (SqlTypeUtil.isInterval(relDataType)) {
+      if (resultValue instanceof BigDecimal) {
+        return toIntervalValue((BigDecimal) resultValue, relDataType);
+      }
+      double value = ((Number) resultValue).doubleValue();
+      Preconditions.checkArgument(Double.isFinite(value), "Invalid interval 
value: %s", value);
+      return toIntervalValue(BigDecimal.valueOf(value), relDataType);
+    }
     // Use double for FLOAT / DOUBLE literals
     if (relDataType.getSqlTypeName() == SqlTypeName.FLOAT || 
relDataType.getSqlTypeName() == SqlTypeName.DOUBLE
         || relDataType.getSqlTypeName() == SqlTypeName.REAL) {
diff --git 
a/pinot-query-planner/src/test/resources/queries/LiteralEvaluationPlans.json 
b/pinot-query-planner/src/test/resources/queries/LiteralEvaluationPlans.json
index 5decbcd0408..2f67b0d0bff 100644
--- a/pinot-query-planner/src/test/resources/queries/LiteralEvaluationPlans.json
+++ b/pinot-query-planner/src/test/resources/queries/LiteralEvaluationPlans.json
@@ -283,10 +283,275 @@
           "\n"
         ]
       },
+      {
+        "description": "timestamp plus year interval",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-01-01 00:00:00' + 
INTERVAL '1' YEAR FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2020-01-01 00:00:00])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "timestamp plus month interval",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-01-01 00:00:00' + 
INTERVAL '1' MONTH FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2019-02-01 00:00:00])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "timestamp plus month interval at end of month",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-01-31 10:00:00' + 
INTERVAL '1' MONTH FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2019-02-28 10:00:00])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "timestamp plus year to month interval",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-01-01 00:00:00' + 
INTERVAL '1-2' YEAR TO MONTH FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2020-03-01 00:00:00])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "month interval plus timestamp",
+        "sql": "EXPLAIN PLAN FOR SELECT INTERVAL '1' MONTH + TIMESTAMP 
'2019-01-01 00:00:00' FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2019-02-01 00:00:00])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "timestamp minus month interval",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-03-31 00:00:00' - 
INTERVAL '1' MONTH FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2019-02-28 00:00:00])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "timestamp plus negative month interval",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-03-01 00:00:00' + 
INTERVAL '-1' MONTH FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2019-02-01 00:00:00])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "timestamp plus sum of year and month intervals",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-01-01 00:00:00' + 
(INTERVAL '1' YEAR + INTERVAL '2' MONTH) FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2020-03-01 00:00:00])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "timestamp plus multiplied month interval",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-01-01 00:00:00' + 
INTERVAL '1' MONTH * 2 FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2019-03-01 00:00:00])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "timestamp plus month interval times a decimal",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-01-01 00:00:00' + 
INTERVAL '100' MONTH * 0.29 FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2021-06-01 00:00:00])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "timestamp plus month interval times another decimal",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-01-01 00:00:00' + 
INTERVAL '100' MONTH * 1.11 FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2028-04-01 00:00:00])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "timestamp plus year interval divided by an integer",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-01-01 00:00:00' + 
INTERVAL '1' YEAR / 4 FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2019-04-01 00:00:00])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "timestamp plus difference of year and month intervals",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-01-01 00:00:00' + 
(INTERVAL '1' YEAR - INTERVAL '1' MONTH) FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2019-12-01 00:00:00])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "null timestamp plus month interval",
+        "sql": "EXPLAIN PLAN FOR SELECT fromDateTime(CAST(NULL AS VARCHAR), 
'yyyy-MM-dd') + INTERVAL '1' MONTH FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[null:TIMESTAMP(3)])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "timestamp plus null month interval",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-01-01 00:00:00' + 
INTERVAL '1' MONTH * toEpochDays(fromDateTime(CAST(NULL AS VARCHAR), 
'yyyy-MM-dd')) FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[null:TIMESTAMP(0)])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "timestamp plus day interval",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-01-01 00:00:00' + 
INTERVAL '1' DAY FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2019-01-02 00:00:00])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "timestamp plus hour interval",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-01-01 00:00:00' + 
INTERVAL '1' HOUR FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2019-01-01 01:00:00])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "timestamp minus day to second interval",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-01-02 00:00:00' - 
INTERVAL '1 01:30:15' DAY TO SECOND FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2018-12-31 22:29:45])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "timestamp plus multiplied day interval",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-01-01 00:00:00' + 
INTERVAL '1' DAY * 2 FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2019-01-03 00:00:00])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "timestamp plus day interval times a decimal",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-01-01 00:00:00' + 
INTERVAL '1' DAY * 0.7 FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2019-01-01 16:48:00])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "filter with timestamp minus month interval",
+        "sql": "EXPLAIN PLAN FOR SELECT * FROM a WHERE ts_timestamp > 
TIMESTAMP '2019-03-01 00:00:00' - INTERVAL '1' MONTH",
+        "output": [
+          "Execution Plan",
+          "\nLogicalFilter(condition=[>($8, 2019-02-01 00:00:00)])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "filter with timestamp minus day interval",
+        "sql": "EXPLAIN PLAN FOR SELECT * FROM a WHERE ts_timestamp > 
TIMESTAMP '2019-03-01 00:00:00' - INTERVAL '1' DAY",
+        "output": [
+          "Execution Plan",
+          "\nLogicalFilter(condition=[>($8, 2019-02-28 00:00:00)])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "date plus day interval",
+        "sql": "EXPLAIN PLAN FOR SELECT DATE '2019-01-01' + INTERVAL '1' DAY 
FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2019-01-02])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "date plus month interval",
+        "sql": "EXPLAIN PLAN FOR SELECT DATE '2019-01-31' + INTERVAL '1' MONTH 
FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2019-02-28])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "date minus year interval",
+        "sql": "EXPLAIN PLAN FOR SELECT DATE '2020-02-29' - INTERVAL '1' YEAR 
FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2019-02-28])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
+      {
+        "description": "date minus hour interval adds only whole days",
+        "sql": "EXPLAIN PLAN FOR SELECT DATE '2019-01-01' - INTERVAL '25' HOUR 
FROM a",
+        "output": [
+          "Execution Plan",
+          "\nLogicalProject(EXPR$0=[2018-12-31])",
+          "\n  PinotLogicalTableScan(table=[[default, a]])",
+          "\n"
+        ]
+      },
       {
         "description": "select non-exist literal function",
         "sql": "EXPLAIN PLAN FOR Select nonExistFun(1,2) FROM a",
         "expectedException": ".*No match found for function signature 
nonExistFun.*"
+      },
+      {
+        "description": "timestamp plus fraction of a month interval",
+        "sql": "EXPLAIN PLAN FOR SELECT TIMESTAMP '2019-01-01 00:00:00' + 
INTERVAL '1' MONTH / 2 FROM a",
+        "expectedException": ".*Year-month interval must be a whole number of 
months, got: 0.5.*"
       }
     ]
   },


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to