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

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


The following commit(s) were added to refs/heads/master by this push:
     new 67a6224d91b Fix up incorrect `PARTITIONED BY` error messages (#15961)
67a6224d91b is described below

commit 67a6224d91bf1d64cdd2aee63ec9652d3106e7d0
Author: Abhishek Radhakrishnan <[email protected]>
AuthorDate: Tue Feb 27 00:47:53 2024 +0530

    Fix up incorrect `PARTITIONED BY` error messages (#15961)
    
    * Fix up typos, inaccuracies and clean up code related to PARTITIONED BY.
    
    * Remove wrapper function and update tests to use DruidExceptionMatcher.
    
    * Checkstyle and Intellij inspection fixes.
---
 docs/multi-stage-query/reference.md                |  10 +-
 sql/src/main/codegen/includes/common.ftl           |   2 +-
 sql/src/main/codegen/includes/insert.ftl           |   2 +-
 sql/src/main/codegen/includes/replace.ftl          |   2 +-
 .../druid/sql/calcite/parser/DruidSqlInsert.java   |   9 +-
 .../sql/calcite/parser/DruidSqlParserUtils.java    | 200 +++++++++------------
 .../druid/sql/calcite/parser/DruidSqlReplace.java  |   9 +-
 .../sql/calcite/parser/SqlGranularityLiteral.java  |  19 +-
 .../druid/sql/calcite/CalciteInsertDmlTest.java    |  63 ++++++-
 .../druid/sql/calcite/CalciteReplaceDmlTest.java   |  57 +++++-
 .../calcite/parser/DruidSqlParserUtilsTest.java    |  92 +++++-----
 11 files changed, 263 insertions(+), 202 deletions(-)

diff --git a/docs/multi-stage-query/reference.md 
b/docs/multi-stage-query/reference.md
index f03ac25b157..115a9e60050 100644
--- a/docs/multi-stage-query/reference.md
+++ b/docs/multi-stage-query/reference.md
@@ -256,12 +256,12 @@ For more information, see [Overwrite data with 
REPLACE](concepts.md#replace).
 ### `PARTITIONED BY`
 
 The `PARTITIONED BY <time granularity>` clause is required for 
[INSERT](#insert) and [REPLACE](#replace). See
-[Partitioning](concepts.md#partitioning) for details.
+[Partitioning](concepts.md#partitioning-by-time) for details.
 
 The following granularity arguments are accepted:
 
 - Time unit keywords: `HOUR`, `DAY`, `MONTH`, or `YEAR`. Equivalent to 
`FLOOR(__time TO TimeUnit)`.
-- Time units as ISO 8601 period strings: :`'PT1H'`, '`P1D`, etc. (Druid 26.0 
and later.)
+- Time units as ISO 8601 period strings: `'PT1H'`, `'P1D'`, etc. (Druid 26.0 
and later.)
 - `TIME_FLOOR(__time, 'granularity_string')`, where granularity_string is one 
of the ISO 8601 periods listed below. The
   first argument must be `__time`.
 - `FLOOR(__time TO TimeUnit)`, where `TimeUnit` is any unit supported by the 
[FLOOR function](../querying/sql-scalar.md#date-and-time-functions). The first 
argument must be `__time`.
@@ -296,8 +296,6 @@ The string constant can also include any of the keywords 
mentioned above:
 - `ALL TIME`
 - `ALL` - Alias for `ALL TIME`
 
-The `WEEK` granularity is deprecated and not supported in MSQ.
-
 Examples:
 
 ```SQL
@@ -411,7 +409,7 @@ The query reads `products` and `customers` and then 
broadcasts both to
 the stage that reads `orders`. That stage loads the broadcast inputs 
(`products` and `customers`) in memory and walks
 through `orders` row by row. The results are aggregated and written to the 
table `orders_enriched`. 
 
-```
+```sql
 REPLACE INTO orders_enriched
 OVERWRITE ALL
 SELECT
@@ -448,7 +446,7 @@ When using the sort-merge algorithm, keep the following in 
mind:
 The following example  runs using a single sort-merge join stage that receives 
`eventstream`
 (partitioned on `user_id`) and `users` (partitioned on `id`) as inputs. There 
is no limit on the size of either input.
 
-```
+```sql
 REPLACE INTO eventstream_enriched
 OVERWRITE ALL
 SELECT
diff --git a/sql/src/main/codegen/includes/common.ftl 
b/sql/src/main/codegen/includes/common.ftl
index 1edc542dc99..0180f671c1d 100644
--- a/sql/src/main/codegen/includes/common.ftl
+++ b/sql/src/main/codegen/includes/common.ftl
@@ -63,7 +63,7 @@ SqlGranularityLiteral PartitionGranularity() :
   |
     e = Expression(ExprContext.ACCEPT_SUB_QUERY)
     {
-      granularity = 
DruidSqlParserUtils.convertSqlNodeToGranularityThrowingParseExceptions(e);
+      granularity = DruidSqlParserUtils.convertSqlNodeToGranularity(e);
       unparseString = e.toString();
     }
   )
diff --git a/sql/src/main/codegen/includes/insert.ftl 
b/sql/src/main/codegen/includes/insert.ftl
index 0a949aec433..323757dd78d 100644
--- a/sql/src/main/codegen/includes/insert.ftl
+++ b/sql/src/main/codegen/includes/insert.ftl
@@ -84,7 +84,7 @@ SqlNode DruidSqlInsertEof() :
     source = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY)
   )
   // PARTITIONED BY is necessary, but is kept optional in the grammar. It is 
asserted that it is not missing in the
-  // DruidSqlInsert constructor so that we can return a custom error message.
+  // IngestHandler#validate() so that we can return a custom error message.
   [
     <PARTITIONED> <BY>
     partitionedBy = PartitionGranularity()
diff --git a/sql/src/main/codegen/includes/replace.ftl 
b/sql/src/main/codegen/includes/replace.ftl
index d067bc450bb..b8851167036 100644
--- a/sql/src/main/codegen/includes/replace.ftl
+++ b/sql/src/main/codegen/includes/replace.ftl
@@ -68,7 +68,7 @@ SqlNode DruidSqlReplaceEof() :
     ]
     source = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY)
     // PARTITIONED BY is necessary, but is kept optional in the grammar. It is 
asserted that it is not missing in the
-    // DruidSqlInsert constructor so that we can return a custom error message.
+    // IngestHandler#validate() so that we can return a custom error message.
     [
       <PARTITIONED> <BY>
       partitionedBy = PartitionGranularity()
diff --git 
a/sql/src/main/java/org/apache/druid/sql/calcite/parser/DruidSqlInsert.java 
b/sql/src/main/java/org/apache/druid/sql/calcite/parser/DruidSqlInsert.java
index e283c9df958..1dc77885263 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/parser/DruidSqlInsert.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/parser/DruidSqlInsert.java
@@ -63,10 +63,11 @@ public class DruidSqlInsert extends DruidSqlIngest
   }
 
   /**
-   * While partitionedBy can be null as arguments to the constructor, this is
-   * disallowed (semantically) and the constructor performs checks to ensure 
that. This helps in producing friendly
-   * errors when the PARTITIONED BY custom clause is not present, and keeps 
its error separate from JavaCC/Calcite's
-   * custom errors which can be cryptic when someone accidentally forgets to 
explicitly specify the PARTITIONED BY clause
+   * While partitionedBy can be null as arguments to the constructor, this is 
disallowed (semantically) and
+   * {@link org.apache.druid.sql.calcite.planner.IngestHandler#validate()} 
performs checks to ensure that. This helps
+   * in producing friendly errors when the PARTITIONED BY custom clause is not 
present, and keeps its error separate
+   * from JavaCC/Calcite's custom errors which can be cryptic when someone 
accidentally forgets to explicitly specify
+   * the PARTITIONED BY clause
    */
   public DruidSqlInsert(
       SqlParserPos pos,
diff --git 
a/sql/src/main/java/org/apache/druid/sql/calcite/parser/DruidSqlParserUtils.java
 
b/sql/src/main/java/org/apache/druid/sql/calcite/parser/DruidSqlParserUtils.java
index fcc7724ada4..5abc866dcbf 100644
--- 
a/sql/src/main/java/org/apache/druid/sql/calcite/parser/DruidSqlParserUtils.java
+++ 
b/sql/src/main/java/org/apache/druid/sql/calcite/parser/DruidSqlParserUtils.java
@@ -20,7 +20,6 @@
 package org.apache.druid.sql.calcite.parser;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import org.apache.calcite.sql.SqlAsOperator;
@@ -37,13 +36,11 @@ import org.apache.calcite.sql.SqlOrderBy;
 import org.apache.calcite.sql.SqlTimestampLiteral;
 import org.apache.calcite.sql.SqlUnknownLiteral;
 import org.apache.calcite.sql.dialect.CalciteSqlDialect;
-import org.apache.calcite.sql.parser.SqlParserUtil;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.type.SqlTypeName;
-import org.apache.calcite.tools.ValidationException;
 import org.apache.calcite.util.Pair;
 import org.apache.druid.error.DruidException;
 import org.apache.druid.error.InvalidSqlInput;
-import org.apache.druid.java.util.common.StringUtils;
 import org.apache.druid.java.util.common.granularity.Granularity;
 import org.apache.druid.java.util.common.granularity.GranularityType;
 import org.apache.druid.java.util.common.granularity.PeriodGranularity;
@@ -78,40 +75,16 @@ public class DruidSqlParserUtils
   private static final Logger log = new Logger(DruidSqlParserUtils.class);
   public static final String ALL = "all";
 
-  private static final List<GranularityType> DOCUMENTED_GRANULARTIES = 
Arrays.stream(GranularityType.values())
-      .filter(g -> g != GranularityType.WEEK)
-      .collect(Collectors.toList());
-  @VisibleForTesting
-  public static final String PARTITION_ERROR_MESSAGE =
-      "Invalid granularity[%s] specified after PARTITIONED BY clause.  "
-      + "Expected "
-      + 
StringUtils.replace(StringUtils.replace(DOCUMENTED_GRANULARTIES.toString(), 
"[", ""), "]", ",").trim()
-      + " ALL TIME, FLOOR() or TIME_FLOOR()";
-
-  /**
-   * Delegates to {@code convertSqlNodeToGranularity} and converts the 
exceptions to {@link ParseException}
-   * with the underlying message
-   */
-  public static Granularity 
convertSqlNodeToGranularityThrowingParseExceptions(SqlNode sqlNode) throws 
ParseException
-  {
-    try {
-      return convertSqlNodeToGranularity(sqlNode);
-    }
-    catch (DruidException e) {
-      throw e;
-    }
-    catch (Exception e) {
-      log.debug(e, StringUtils.format("Unable to convert %s to a valid 
granularity.", sqlNode.toString()));
-      throw new ParseException(e.getMessage());
-    }
-  }
+  private static final List<GranularityType> DOCUMENTED_GRANULARITIES = 
Arrays.stream(GranularityType.values())
+                                                                              
.filter(g -> g != GranularityType.WEEK &&
+                                                                               
            g != GranularityType.NONE)
+                                                                              
.collect(Collectors.toList());
 
   /**
    * This method is used to extract the granularity from a SqlNode which 
represents
    * the argument to the {@code PARTITIONED BY} clause. The node can be any of 
the following:
    * <ul>
-   * <li>A literal with a string that matches the SQL keywords
-   * {@code HOUR, DAY, MONTH, YEAR, ALL [TIME]}</li>
+   * <li>A literal with a string that matches the SQL keywords from {@link 
#DOCUMENTED_GRANULARITIES} </li>
    * <li>A literal string with a period in ISO 8601 format.</li>
    * <li>Function call: {@code FLOOR(__time TO TimeUnit)}</li>
    * <li>Function call: TIME_FLOOR(__time, 'PT1H')}</li>
@@ -143,22 +116,23 @@ public class DruidSqlParserUtils
       return null;
     }
 
+    final Granularity retVal;
+
+    // Check if argument is a literal such as DAY or "DAY".
     if (sqlNode instanceof SqlLiteral) {
-      final Granularity retVal;
       SqlLiteral literal = (SqlLiteral) sqlNode;
       if (SqlLiteral.valueMatchesType(literal.getValue(), SqlTypeName.CHAR)) {
         retVal = convertSqlLiteralCharToGranularity(literal);
       } else {
         throw makeInvalidPartitionByException(literal);
       }
-
       validateSupportedGranularityForPartitionedBy(sqlNode, retVal);
       return retVal;
     }
 
+    // Check if argument is an ISO 8601 period such as P1D or "P1D".
     if (sqlNode instanceof SqlIdentifier) {
       SqlIdentifier identifier = (SqlIdentifier) sqlNode;
-      final Granularity retVal;
       retVal = convertSqlIdentiferToGranularity(identifier);
       validateSupportedGranularityForPartitionedBy(sqlNode, retVal);
       return retVal;
@@ -171,41 +145,56 @@ public class DruidSqlParserUtils
 
     String operatorName = sqlCall.getOperator().getName();
 
-    Preconditions.checkArgument(
-        "FLOOR".equalsIgnoreCase(operatorName)
-        || 
TimeFloorOperatorConversion.SQL_FUNCTION_NAME.equalsIgnoreCase(operatorName),
-        StringUtils.format(
-            "PARTITIONED BY clause only supports FLOOR(__time TO <unit> and 
%s(__time, period) functions",
-            TimeFloorOperatorConversion.SQL_FUNCTION_NAME
-        )
-    );
+    if (!(SqlStdOperatorTable.FLOOR.getName().equalsIgnoreCase(operatorName) ||
+          
TimeFloorOperatorConversion.SQL_FUNCTION_NAME.equalsIgnoreCase(operatorName))) {
+      throw InvalidSqlInput.exception(
+          "Invalid operator[%s] specified. "
+          + "PARTITIONED BY clause only supports %s(__time TO <unit>) and 
%s(__time, period) functions.",
+          operatorName,
+          SqlStdOperatorTable.FLOOR.getName(),
+          TimeFloorOperatorConversion.SQL_FUNCTION_NAME
+      );
+    }
 
     List<SqlNode> operandList = sqlCall.getOperandList();
-    Preconditions.checkArgument(
-        operandList.size() == 2,
-        StringUtils.format("%s in PARTITIONED BY clause must have two 
arguments", operatorName)
-    );
-
+    if (operandList.size() != 2) {
+      throw InvalidSqlInput.exception(
+          "%s in PARTITIONED BY clause must have 2 arguments, but only [%d] 
provided.",
+          operatorName,
+          operandList.size()
+      );
+    }
 
     // Check if the first argument passed in the floor function is __time
     SqlNode timeOperandSqlNode = operandList.get(0);
-    Preconditions.checkArgument(
-        timeOperandSqlNode.getKind().equals(SqlKind.IDENTIFIER),
-        StringUtils.format("First argument to %s in PARTITIONED BY clause can 
only be __time", operatorName)
-    );
+    if (!SqlKind.IDENTIFIER.equals(timeOperandSqlNode.getKind())) {
+      throw InvalidSqlInput.exception(
+          "Invalid argument type[%s] provided. The first argument to %s in 
PARTITIONED BY clause must be __time.",
+          timeOperandSqlNode.getKind(),
+          operatorName
+      );
+    }
+
     SqlIdentifier timeOperandSqlIdentifier = (SqlIdentifier) 
timeOperandSqlNode;
-    Preconditions.checkArgument(
-        
timeOperandSqlIdentifier.getSimple().equals(ColumnHolder.TIME_COLUMN_NAME),
-        StringUtils.format("First argument to %s in PARTITIONED BY clause can 
only be __time", operatorName)
-    );
+    if 
(!ColumnHolder.TIME_COLUMN_NAME.equals(timeOperandSqlIdentifier.getSimple())) {
+      throw InvalidSqlInput.exception(
+          "Invalid argument[%s] provided. The first argument to %s in 
PARTITIONED BY clause must be __time.",
+          timeOperandSqlIdentifier.getSimple(),
+          operatorName
+      );
+    }
 
     // If the floor function is of form TIME_FLOOR(__time, 'PT1H')
-    if 
(operatorName.equalsIgnoreCase(TimeFloorOperatorConversion.SQL_FUNCTION_NAME)) {
+    if 
(TimeFloorOperatorConversion.SQL_FUNCTION_NAME.equalsIgnoreCase(operatorName)) {
       SqlNode granularitySqlNode = operandList.get(1);
-      Preconditions.checkArgument(
-          granularitySqlNode.getKind().equals(SqlKind.LITERAL),
-          "Second argument to TIME_FLOOR in PARTITIONED BY clause must be a 
period like 'PT1H'"
-      );
+      if (!SqlKind.LITERAL.equals(granularitySqlNode.getKind())) {
+        throw InvalidSqlInput.exception(
+            "Invalid argument[%s] provided. The second argument to %s in 
PARTITIONED BY clause must be a period like 'PT1H'.",
+            granularitySqlNode.getKind(),
+            TimeFloorOperatorConversion.SQL_FUNCTION_NAME
+        );
+      }
+
       String granularityString = 
SqlLiteral.unchain(granularitySqlNode).toValue();
       Period period;
       try {
@@ -213,33 +202,35 @@ public class DruidSqlParserUtils
       }
       catch (IllegalArgumentException e) {
         throw InvalidSqlInput.exception(
-            StringUtils.format("granularity[%s] is an invalid period string", 
granularitySqlNode.toString()),
-            sqlNode);
+            "granularity[%s] is an invalid period literal.",
+            granularitySqlNode.toString()
+        );
       }
-      final PeriodGranularity retVal = new PeriodGranularity(period, null, 
null);
+      retVal = new PeriodGranularity(period, null, null);
       validateSupportedGranularityForPartitionedBy(sqlNode, retVal);
       return retVal;
 
-    } else if ("FLOOR".equalsIgnoreCase(operatorName)) { // If the floor 
function is of form FLOOR(__time TO DAY)
+    } else if 
(SqlStdOperatorTable.FLOOR.getName().equalsIgnoreCase(operatorName)) { // If 
the floor function is of form FLOOR(__time TO DAY)
       SqlNode granularitySqlNode = operandList.get(1);
       // In future versions of Calcite, this can be checked via
       // granularitySqlNode.getKind().equals(SqlKind.INTERVAL_QUALIFIER)
-      Preconditions.checkArgument(
-          granularitySqlNode instanceof SqlIntervalQualifier,
-          "Second argument to the FLOOR function in PARTITIONED BY clause is 
not a valid granularity. "
-          + "Please refer to the documentation of FLOOR function"
-      );
+      if (!(granularitySqlNode instanceof SqlIntervalQualifier)) {
+        throw InvalidSqlInput.exception(
+            "Second argument[%s] to the FLOOR function in PARTITIONED BY 
clause is not a valid granularity. "
+            + "Please refer to the documentation of FLOOR function.",
+            granularitySqlNode.toString()
+        );
+      }
       SqlIntervalQualifier granularityIntervalQualifier = 
(SqlIntervalQualifier) granularitySqlNode;
 
       Period period = 
TimeUnits.toPeriod(granularityIntervalQualifier.timeUnitRange);
-      Preconditions.checkNotNull(
-          period,
-          StringUtils.format(
-              "%s is not a valid granularity for ingestion",
-              granularityIntervalQualifier.timeUnitRange.toString()
-          )
-      );
-      final PeriodGranularity retVal = new PeriodGranularity(period, null, 
null);
+      if (period == null) {
+        throw InvalidSqlInput.exception(
+            "%s is not a valid period granularity for ingestion.",
+            granularityIntervalQualifier.timeUnitRange.toString()
+        );
+      }
+      retVal = new PeriodGranularity(period, null, null);
       validateSupportedGranularityForPartitionedBy(sqlNode, retVal);
       return retVal;
     }
@@ -286,7 +277,11 @@ public class DruidSqlParserUtils
   private static DruidException makeInvalidPartitionByException(SqlNode 
sqlNode)
   {
     return InvalidSqlInput.exception(
-        PARTITION_ERROR_MESSAGE,
+        "Invalid granularity[%s] specified after PARTITIONED BY clause. 
Expected "
+        + DOCUMENTED_GRANULARITIES.stream()
+                                  .map(granularityType -> "'" + 
granularityType.name() + "'")
+                                  .collect(Collectors.joining(", "))
+        + ", ALL TIME, FLOOR() or TIME_FLOOR()",
         sqlNode
     );
   }
@@ -307,7 +302,7 @@ public class DruidSqlParserUtils
    * @param granularity      granularity of the query for validation
    * @param dateTimeZone     timezone
    * @return List of string representation of intervals
-   * @throws ValidationException if the SqlNode cannot be converted to a list 
of intervals
+   * @throws DruidException if the SqlNode cannot be converted to a list of 
intervals
    */
   public static List<String> validateQueryAndConvertToIntervals(
       SqlNode replaceTimeQuery,
@@ -365,7 +360,7 @@ public class DruidSqlParserUtils
    * @param query           sql query
    * @param clusteredByList List of clustered by columns
    * @return SqlOrderBy node containing the clusteredByList information
-   * @throws ValidationException if any of the clustered by columns contain 
DESCENDING order.
+   * @throws DruidException if any of the clustered by columns contain 
DESCENDING order.
    */
   public static SqlOrderBy convertClusterByToOrderBy(SqlNode query, 
SqlNodeList clusteredByList)
   {
@@ -472,6 +467,7 @@ public class DruidSqlParserUtils
    *
    * @param clusteredByNodes List of SqlNodes representing columns to be 
clustered by.
    */
+  @VisibleForTesting
   public static void validateClusteredByColumns(final SqlNodeList 
clusteredByNodes)
   {
     if (clusteredByNodes == null) {
@@ -508,9 +504,9 @@ public class DruidSqlParserUtils
    * @param replaceTimeQuery Sql node representing the query
    * @param dateTimeZone     timezone
    * @return Dimfilter for the query
-   * @throws ValidationException if the SqlNode cannot be converted a Dimfilter
+   * @throws DruidException if the SqlNode cannot be converted a Dimfilter
    */
-  public static DimFilter convertQueryToDimFilter(SqlNode replaceTimeQuery, 
DateTimeZone dateTimeZone)
+  private static DimFilter convertQueryToDimFilter(SqlNode replaceTimeQuery, 
DateTimeZone dateTimeZone)
   {
     if (!(replaceTimeQuery instanceof SqlBasicCall)) {
       throw InvalidSqlInput.exception(
@@ -667,44 +663,10 @@ public class DruidSqlParserUtils
   )
   {
     if (!GranularityType.isStandard(granularity)) {
-      throw InvalidSqlInput.exception(
-          "The granularity specified in PARTITIONED BY [%s] is not supported.  
Valid options: [%s]",
-          originalNode == null ? granularity : originalNode,
-          Arrays.stream(GranularityType.values())
-                .filter(granularityType -> 
!granularityType.equals(GranularityType.NONE))
-                .map(Enum::name)
-                .map(StringUtils::toLowerCase)
-                .collect(Collectors.joining(", "))
-      );
+      throw makeInvalidPartitionByException(originalNode);
     }
   }
 
-  /**
-   * Get the timestamp string from a TIMESTAMP (or TIMESTAMP WITH LOCAL TIME 
ZONE) literal.
-   *
-   * @return string, or null if the provided node is not a timestamp literal
-   */
-  @Nullable
-  private static String getTimestampStringFromLiteral(final SqlNode sqlNode)
-  {
-    if (sqlNode instanceof SqlTimestampLiteral) {
-      return ((SqlTimestampLiteral) sqlNode).toFormattedString();
-    }
-
-    if (sqlNode instanceof SqlUnknownLiteral) {
-      // SqlUnknownLiteral represents a type that is unknown until validation 
time. The tag is resolved to a proper
-      // type name later on; for example TIMESTAMP may become TIMESTAMP WITH 
LOCAL TIME ZONE.
-      final SqlUnknownLiteral sqlUnknownLiteral = (SqlUnknownLiteral) sqlNode;
-
-      if (SqlTypeName.TIMESTAMP.getSpaceName().equals(sqlUnknownLiteral.tag)
-          || 
SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE.getSpaceName().equals(sqlUnknownLiteral.tag))
 {
-        return 
SqlParserUtil.parseTimestampLiteral(sqlUnknownLiteral.getValue(), 
sqlNode.getParserPosition())
-                            .toFormattedString();
-      }
-    }
-
-    return null;
-  }
 
   public static DruidException problemParsing(String message)
   {
diff --git 
a/sql/src/main/java/org/apache/druid/sql/calcite/parser/DruidSqlReplace.java 
b/sql/src/main/java/org/apache/druid/sql/calcite/parser/DruidSqlReplace.java
index 45b677631d2..4312a9f684f 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/parser/DruidSqlReplace.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/parser/DruidSqlReplace.java
@@ -69,10 +69,11 @@ public class DruidSqlReplace extends DruidSqlIngest
   }
 
   /**
-   * While partitionedBy can be null as arguments to the constructor, this is
-   * disallowed (semantically) and the constructor performs checks to ensure 
that. This helps in producing friendly
-   * errors when the PARTITIONED BY custom clause is not present, and keeps 
its error separate from JavaCC/Calcite's
-   * custom errors which can be cryptic when someone accidentally forgets to 
explicitly specify the PARTITIONED BY clause
+   * While partitionedBy can be null as arguments to the constructor, this is 
disallowed (semantically) and
+   * {@link org.apache.druid.sql.calcite.planner.IngestHandler#validate()} 
performs checks to ensure that. This helps
+   * in producing friendly errors when the PARTITIONED BY custom clause is not 
present, and keeps its error separate
+   * from JavaCC/Calcite's custom errors which can be cryptic when someone 
accidentally forgets to explicitly specify
+   * the PARTITIONED BY clause
    */
   public DruidSqlReplace(
       SqlParserPos pos,
diff --git 
a/sql/src/main/java/org/apache/druid/sql/calcite/parser/SqlGranularityLiteral.java
 
b/sql/src/main/java/org/apache/druid/sql/calcite/parser/SqlGranularityLiteral.java
index 3fefb046948..364d6930eeb 100644
--- 
a/sql/src/main/java/org/apache/druid/sql/calcite/parser/SqlGranularityLiteral.java
+++ 
b/sql/src/main/java/org/apache/druid/sql/calcite/parser/SqlGranularityLiteral.java
@@ -19,24 +19,22 @@
 
 package org.apache.druid.sql.calcite.parser;
 
-import org.apache.calcite.sql.SqlIdentifier;
 import org.apache.calcite.sql.SqlLiteral;
 import org.apache.calcite.sql.SqlWriter;
 import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.util.NlsString;
-import org.apache.druid.error.DruidException;
 import org.apache.druid.java.util.common.granularity.Granularity;
 
 import javax.annotation.Nonnull;
 
 /**
- * Extends the {@link SqlIdentifier} to hold parameters for the PARTITIONED BY 
clause.
+ * Extends the {@link SqlLiteral} to hold parameters for the PARTITIONED BY 
clause.
  */
 public class SqlGranularityLiteral extends SqlLiteral
 {
-  private String unparseString;
-  private Granularity granularity;
+  private final String unparseString;
+  private final Granularity granularity;
 
   public SqlGranularityLiteral(
       @Nonnull Granularity granularity,
@@ -54,13 +52,6 @@ public class SqlGranularityLiteral extends SqlLiteral
     return new SqlGranularityLiteral(granularity, unparseString, pos);
   }
 
-  @Override
-  @Deprecated
-  public Object clone()
-  {
-    throw DruidException.defensive("Function is deprecated, please use 
clone(SqlNode) instead.");
-  }
-
   @Nonnull
   public Granularity getGranularity()
   {
@@ -70,8 +61,6 @@ public class SqlGranularityLiteral extends SqlLiteral
   @Override
   public void unparse(SqlWriter writer, int leftPrec, int rightPrec)
   {
-    if (unparseString != null) {
-      writer.keyword(unparseString);
-    }
+    writer.keyword(unparseString);
   }
 }
diff --git 
a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteInsertDmlTest.java 
b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteInsertDmlTest.java
index 53e86c239de..81e32895124 100644
--- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteInsertDmlTest.java
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteInsertDmlTest.java
@@ -31,6 +31,7 @@ import org.apache.druid.error.DruidExceptionMatcher;
 import org.apache.druid.java.util.common.StringUtils;
 import org.apache.druid.java.util.common.granularity.Granularities;
 import org.apache.druid.java.util.common.granularity.Granularity;
+import org.apache.druid.java.util.common.granularity.GranularityType;
 import org.apache.druid.java.util.common.jackson.JacksonUtils;
 import org.apache.druid.query.QueryDataSource;
 import org.apache.druid.query.aggregation.CountAggregatorFactory;
@@ -47,7 +48,6 @@ import 
org.apache.druid.sql.calcite.external.ExternalDataSource;
 import org.apache.druid.sql.calcite.external.Externals;
 import org.apache.druid.sql.calcite.filtration.Filtration;
 import org.apache.druid.sql.calcite.parser.DruidSqlInsert;
-import org.apache.druid.sql.calcite.parser.DruidSqlParserUtils;
 import org.apache.druid.sql.calcite.planner.Calcites;
 import org.apache.druid.sql.calcite.planner.PlannerContext;
 import org.apache.druid.sql.calcite.util.CalciteTests;
@@ -59,9 +59,11 @@ import org.junit.internal.matchers.ThrowableMessageMatcher;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import static org.apache.druid.segment.column.ColumnType.DOUBLE;
 import static org.apache.druid.segment.column.ColumnType.FLOAT;
@@ -657,6 +659,52 @@ public class CalciteInsertDmlTest extends 
CalciteIngestionDmlTest
     didTest = true;
   }
 
+  @Test
+  public void testPartitionedBySupportedGranularityLiteralClauses()
+  {
+    final RowSignature targetRowSignature = RowSignature.builder()
+                                                        .add("__time", 
ColumnType.LONG)
+                                                        .add("dim1", 
ColumnType.STRING)
+                                                        .build();
+
+    final Map<String, Granularity> partitionedByToGranularity =
+        Arrays.stream(GranularityType.values())
+              .collect(Collectors.toMap(GranularityType::name, 
GranularityType::getDefaultGranularity));
+
+    final ObjectMapper queryJsonMapper = queryFramework().queryJsonMapper();
+    partitionedByToGranularity.forEach((partitionedByArgument, 
expectedGranularity) -> {
+      Map<String, Object> queryContext = null;
+      try {
+        queryContext = ImmutableMap.of(
+            DruidSqlInsert.SQL_INSERT_SEGMENT_GRANULARITY, 
queryJsonMapper.writeValueAsString(expectedGranularity)
+        );
+      }
+      catch (JsonProcessingException e) {
+        // Won't reach here
+        Assert.fail(e.getMessage());
+      }
+
+      testIngestionQuery()
+          .sql(StringUtils.format(
+              "INSERT INTO druid.dst SELECT __time, dim1 FROM foo PARTITIONED 
BY '%s'",
+              partitionedByArgument
+          ))
+          .expectTarget("dst", targetRowSignature)
+          .expectResources(dataSourceRead("foo"), dataSourceWrite("dst"))
+          .expectQuery(
+              newScanQueryBuilder()
+                  .dataSource("foo")
+                  .intervals(querySegmentSpec(Filtration.eternity()))
+                  .columns("__time", "dim1")
+                  .context(queryContext)
+                  .build()
+          )
+          .verify();
+      didTest = false;
+    });
+    didTest = true;
+  }
+
   @Test
   public void testExplainPlanInsertWithClusteredBy() throws 
JsonProcessingException
   {
@@ -1165,7 +1213,6 @@ public class CalciteInsertDmlTest extends 
CalciteIngestionDmlTest
   @Test
   public void testInsertWithPartitionedByContainingInvalidGranularity()
   {
-    // Throws a ValidationException, which gets converted to a DruidException 
before throwing to end user
     try {
       testQuery(
           "INSERT INTO dst SELECT * FROM foo PARTITIONED BY 
'invalid_granularity'",
@@ -1178,7 +1225,10 @@ public class CalciteInsertDmlTest extends 
CalciteIngestionDmlTest
       MatcherAssert.assertThat(
           e,
           invalidSqlIs(
-              StringUtils.format(DruidSqlParserUtils.PARTITION_ERROR_MESSAGE, 
"'invalid_granularity'")
+              "Invalid granularity['invalid_granularity'] specified after 
PARTITIONED BY clause."
+              + " Expected 'SECOND', 'MINUTE', 'FIVE_MINUTE', 'TEN_MINUTE', 
'FIFTEEN_MINUTE', 'THIRTY_MINUTE', 'HOUR',"
+              + " 'SIX_HOUR', 'EIGHT_HOUR', 'DAY', 'MONTH', 'QUARTER', 'YEAR', 
'ALL', ALL TIME, FLOOR()"
+              + " or TIME_FLOOR()"
           ));
     }
     didTest = true;
@@ -1624,9 +1674,10 @@ public class CalciteInsertDmlTest extends 
CalciteIngestionDmlTest
             CoreMatchers.allOf(
                 CoreMatchers.instanceOf(DruidException.class),
                 ThrowableMessageMatcher.hasMessage(CoreMatchers.containsString(
-                    "The granularity specified in PARTITIONED BY 
[`time_floor`(`__time`, 'PT2H')] is not supported.  "
-                    + "Valid options: [second, minute, five_minute, 
ten_minute, fifteen_minute, thirty_minute, hour, "
-                    + "six_hour, eight_hour, day, week, month, quarter, year, 
all]"))
+                    "Invalid granularity[`time_floor`(`__time`, 'PT2H')] 
specified after PARTITIONED BY clause."
+                    + " Expected 'SECOND', 'MINUTE', 'FIVE_MINUTE', 
'TEN_MINUTE', 'FIFTEEN_MINUTE', 'THIRTY_MINUTE',"
+                    + " 'HOUR', 'SIX_HOUR', 'EIGHT_HOUR', 'DAY', 'MONTH', 
'QUARTER', 'YEAR', 'ALL',"
+                    + " ALL TIME, FLOOR() or TIME_FLOOR()"))
             )
         )
         .verify();
diff --git 
a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteReplaceDmlTest.java 
b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteReplaceDmlTest.java
index 0b905b47b73..00a93b3fa68 100644
--- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteReplaceDmlTest.java
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteReplaceDmlTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.druid.sql.calcite;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -26,6 +27,8 @@ import org.apache.druid.error.DruidException;
 import org.apache.druid.error.DruidExceptionMatcher;
 import org.apache.druid.java.util.common.StringUtils;
 import org.apache.druid.java.util.common.granularity.Granularities;
+import org.apache.druid.java.util.common.granularity.Granularity;
+import org.apache.druid.java.util.common.granularity.GranularityType;
 import org.apache.druid.java.util.common.jackson.JacksonUtils;
 import org.apache.druid.query.aggregation.CountAggregatorFactory;
 import org.apache.druid.query.aggregation.LongSumAggregatorFactory;
@@ -47,9 +50,11 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import static org.apache.druid.segment.column.ColumnType.DOUBLE;
 import static org.apache.druid.segment.column.ColumnType.FLOAT;
@@ -594,10 +599,55 @@ public class CalciteReplaceDmlTest extends 
CalciteIngestionDmlTest
         .verify();
   }
 
+  @Test
+  public void testPartitionedBySupportedGranularityLiteralClauses()
+  {
+    final RowSignature targetRowSignature = RowSignature.builder()
+                                                        .add("__time", 
ColumnType.LONG)
+                                                        .add("dim1", 
ColumnType.STRING)
+                                                        .build();
+
+    final Map<String, Granularity> partitionedByToGranularity =
+        Arrays.stream(GranularityType.values())
+              .collect(Collectors.toMap(GranularityType::name, 
GranularityType::getDefaultGranularity));
+
+    final ObjectMapper queryJsonMapper = queryFramework().queryJsonMapper();
+    partitionedByToGranularity.forEach((partitionedByArgument, 
expectedGranularity) -> {
+      Map<String, Object> queryContext = null;
+      try {
+        queryContext = ImmutableMap.of(
+            DruidSqlInsert.SQL_INSERT_SEGMENT_GRANULARITY, 
queryJsonMapper.writeValueAsString(expectedGranularity)
+        );
+      }
+      catch (JsonProcessingException e) {
+        // Won't reach here
+        Assert.fail(e.getMessage());
+      }
+
+      testIngestionQuery()
+          .sql(StringUtils.format(
+              "REPLACE INTO druid.dst OVERWRITE ALL SELECT __time, dim1 FROM 
foo PARTITIONED BY '%s'",
+              partitionedByArgument
+          ))
+          .expectTarget("dst", targetRowSignature)
+          .expectResources(dataSourceRead("foo"), dataSourceWrite("dst"))
+          .expectQuery(
+              newScanQueryBuilder()
+                  .dataSource("foo")
+                  .intervals(querySegmentSpec(Filtration.eternity()))
+                  .columns("__time", "dim1")
+                  .context(queryContext)
+                  .build()
+          )
+          .verify();
+      didTest = false;
+    });
+    didTest = true;
+  }
+
   @Test
   public void testReplaceWithPartitionedByContainingInvalidGranularity()
   {
-    // Throws a ValidationException, which gets converted to a DruidException 
before throwing to end user
     try {
       testQuery(
           "REPLACE INTO dst OVERWRITE ALL SELECT * FROM foo PARTITIONED BY 
'invalid_granularity'",
@@ -610,7 +660,10 @@ public class CalciteReplaceDmlTest extends 
CalciteIngestionDmlTest
       MatcherAssert.assertThat(
           e,
           invalidSqlIs(
-              StringUtils.format(DruidSqlParserUtils.PARTITION_ERROR_MESSAGE, 
"'invalid_granularity'")
+              "Invalid granularity['invalid_granularity'] specified after 
PARTITIONED BY clause."
+              + " Expected 'SECOND', 'MINUTE', 'FIVE_MINUTE', 'TEN_MINUTE', 
'FIFTEEN_MINUTE', 'THIRTY_MINUTE', 'HOUR',"
+              + " 'SIX_HOUR', 'EIGHT_HOUR', 'DAY', 'MONTH', 'QUARTER', 'YEAR', 
'ALL', ALL TIME, FLOOR()"
+              + " or TIME_FLOOR()"
           ));
     }
     didTest = true;
diff --git 
a/sql/src/test/java/org/apache/druid/sql/calcite/parser/DruidSqlParserUtilsTest.java
 
b/sql/src/test/java/org/apache/druid/sql/calcite/parser/DruidSqlParserUtilsTest.java
index ce1c1c39b58..5165f75785d 100644
--- 
a/sql/src/test/java/org/apache/druid/sql/calcite/parser/DruidSqlParserUtilsTest.java
+++ 
b/sql/src/test/java/org/apache/druid/sql/calcite/parser/DruidSqlParserUtilsTest.java
@@ -83,13 +83,13 @@ public class DruidSqlParserUtilsTest
     }
 
     @Test
-    public void testGranularityFromTimeFloor() throws ParseException
+    public void testGranularityFromTimeFloor()
     {
       final SqlNodeList args = new SqlNodeList(SqlParserPos.ZERO);
       args.add(new SqlIdentifier("__time", SqlParserPos.ZERO));
       args.add(SqlLiteral.createCharString(this.periodString, 
SqlParserPos.ZERO));
       final SqlNode timeFloorCall = 
TimeFloorOperatorConversion.SQL_FUNCTION.createCall(args);
-      Granularity actualGranularity = 
DruidSqlParserUtils.convertSqlNodeToGranularityThrowingParseExceptions(
+      Granularity actualGranularity = 
DruidSqlParserUtils.convertSqlNodeToGranularity(
           timeFloorCall);
       Assert.assertEquals(expectedGranularity, actualGranularity);
     }
@@ -128,14 +128,14 @@ public class DruidSqlParserUtilsTest
     }
 
     @Test
-    public void testGetGranularityFromFloor() throws ParseException
+    public void testGetGranularityFromFloor()
     {
       // parserPos doesn't matter
       final SqlNodeList args = new SqlNodeList(SqlParserPos.ZERO);
       args.add(new SqlIdentifier("__time", SqlParserPos.ZERO));
       args.add(new SqlIntervalQualifier(this.timeUnit, null, 
SqlParserPos.ZERO));
       final SqlNode floorCall = SqlStdOperatorTable.FLOOR.createCall(args);
-      Granularity actualGranularity = 
DruidSqlParserUtils.convertSqlNodeToGranularityThrowingParseExceptions(floorCall);
+      Granularity actualGranularity = 
DruidSqlParserUtils.convertSqlNodeToGranularity(floorCall);
       Assert.assertEquals(expectedGranularity, actualGranularity);
     }
 
@@ -143,10 +143,10 @@ public class DruidSqlParserUtilsTest
      * Tests clause like "PARTITIONED BY 'day'"
      */
     @Test
-    public void testConvertSqlNodeToGranularityAsLiteral() throws 
ParseException
+    public void testConvertSqlNodeToGranularityAsLiteral()
     {
       SqlNode sqlNode = SqlLiteral.createCharString(timeUnit.name(), 
SqlParserPos.ZERO);
-      Granularity actualGranularity = 
DruidSqlParserUtils.convertSqlNodeToGranularityThrowingParseExceptions(sqlNode);
+      Granularity actualGranularity = 
DruidSqlParserUtils.convertSqlNodeToGranularity(sqlNode);
       Assert.assertEquals(expectedGranularity, actualGranularity);
     }
 
@@ -154,10 +154,10 @@ public class DruidSqlParserUtilsTest
      * Tests clause like "PARTITIONED BY PT1D"
      */
     @Test
-    public void testConvertSqlNodeToPeriodFormGranularityAsIdentifier() throws 
ParseException
+    public void testConvertSqlNodeToPeriodFormGranularityAsIdentifier()
     {
       SqlNode sqlNode = new SqlIdentifier(period.toString(), 
SqlParserPos.ZERO);
-      Granularity actualGranularity = 
DruidSqlParserUtils.convertSqlNodeToGranularityThrowingParseExceptions(sqlNode);
+      Granularity actualGranularity = 
DruidSqlParserUtils.convertSqlNodeToGranularity(sqlNode);
       Assert.assertEquals(expectedGranularity, actualGranularity);
     }
 
@@ -165,10 +165,10 @@ public class DruidSqlParserUtilsTest
      * Tests clause like "PARTITIONED BY 'PT1D'"
      */
     @Test
-    public void testConvertSqlNodeToPeriodFormGranularityAsLiteral() throws 
ParseException
+    public void testConvertSqlNodeToPeriodFormGranularityAsLiteral()
     {
       SqlNode sqlNode = SqlLiteral.createCharString(period.toString(), 
SqlParserPos.ZERO);
-      Granularity actualGranularity = 
DruidSqlParserUtils.convertSqlNodeToGranularityThrowingParseExceptions(sqlNode);
+      Granularity actualGranularity = 
DruidSqlParserUtils.convertSqlNodeToGranularity(sqlNode);
       Assert.assertEquals(expectedGranularity, actualGranularity);
     }
   }
@@ -353,14 +353,13 @@ public class DruidSqlParserUtilsTest
       args.add(new SqlIdentifier("__time", SqlParserPos.ZERO));
       args.add(new SqlIntervalQualifier(TimeUnit.DAY, null, 
SqlParserPos.ZERO));
       final SqlNode sqlNode = SqlStdOperatorTable.CEIL.createCall(args);
-      ParseException e = Assert.assertThrows(
-          ParseException.class,
-          () -> 
DruidSqlParserUtils.convertSqlNodeToGranularityThrowingParseExceptions(sqlNode)
-      );
-      Assert.assertEquals(
-          "PARTITIONED BY clause only supports FLOOR(__time TO <unit> and 
TIME_FLOOR(__time, period) functions",
-          e.getMessage()
-      );
+      DruidExceptionMatcher
+          .invalidSqlInput()
+          .expectMessageIs(
+              "Invalid operator[CEIL] specified. PARTITIONED BY clause only 
supports FLOOR(__time TO <unit>)"
+              + " and TIME_FLOOR(__time, period) functions."
+          )
+          .assertThrowsAndMatches(() -> 
DruidSqlParserUtils.convertSqlNodeToGranularity(sqlNode));
     }
 
     /**
@@ -372,11 +371,12 @@ public class DruidSqlParserUtilsTest
       final SqlNodeList args = new SqlNodeList(SqlParserPos.ZERO);
       args.add(new SqlIdentifier("__time", SqlParserPos.ZERO));
       final SqlNode sqlNode = SqlStdOperatorTable.FLOOR.createCall(args);
-      ParseException e = Assert.assertThrows(
-          ParseException.class,
-          () -> 
DruidSqlParserUtils.convertSqlNodeToGranularityThrowingParseExceptions(sqlNode)
-      );
-      Assert.assertEquals("FLOOR in PARTITIONED BY clause must have two 
arguments", e.getMessage());
+      DruidExceptionMatcher
+          .invalidSqlInput()
+          .expectMessageIs(
+              "FLOOR in PARTITIONED BY clause must have 2 arguments, but only 
[1] provided."
+          )
+          .assertThrowsAndMatches(() -> 
DruidSqlParserUtils.convertSqlNodeToGranularity(sqlNode));
     }
 
     /**
@@ -389,11 +389,13 @@ public class DruidSqlParserUtilsTest
       args.add(new SqlIdentifier("timestamps", SqlParserPos.ZERO));
       args.add(new SqlIntervalQualifier(TimeUnit.DAY, null, 
SqlParserPos.ZERO));
       final SqlNode sqlNode = SqlStdOperatorTable.FLOOR.createCall(args);
-      ParseException e = Assert.assertThrows(
-          ParseException.class,
-          () -> 
DruidSqlParserUtils.convertSqlNodeToGranularityThrowingParseExceptions(sqlNode)
-      );
-      Assert.assertEquals("First argument to FLOOR in PARTITIONED BY clause 
can only be __time", e.getMessage());
+      DruidExceptionMatcher
+          .invalidSqlInput()
+          .expectMessageIs(
+              "Invalid argument[timestamps] provided. The first argument to 
FLOOR in PARTITIONED BY"
+              + " clause must be __time."
+          )
+          .assertThrowsAndMatches(() -> 
DruidSqlParserUtils.convertSqlNodeToGranularity(sqlNode));
     }
 
     /**
@@ -406,11 +408,13 @@ public class DruidSqlParserUtilsTest
       args.add(new SqlIdentifier("timestamps", SqlParserPos.ZERO));
       args.add(SqlLiteral.createCharString("PT1H", SqlParserPos.ZERO));
       final SqlNode sqlNode = 
TimeFloorOperatorConversion.SQL_FUNCTION.createCall(args);
-      ParseException e = Assert.assertThrows(
-          ParseException.class,
-          () -> 
DruidSqlParserUtils.convertSqlNodeToGranularityThrowingParseExceptions(sqlNode)
-      );
-      Assert.assertEquals("First argument to TIME_FLOOR in PARTITIONED BY 
clause can only be __time", e.getMessage());
+      DruidExceptionMatcher
+          .invalidSqlInput()
+          .expectMessageIs(
+              "Invalid argument[timestamps] provided. The first argument to 
TIME_FLOOR in"
+              + " PARTITIONED BY clause must be __time."
+          )
+          .assertThrowsAndMatches(() -> 
DruidSqlParserUtils.convertSqlNodeToGranularity(sqlNode));
     }
 
     /**
@@ -423,11 +427,12 @@ public class DruidSqlParserUtilsTest
       args.add(new SqlIdentifier("__time", SqlParserPos.ZERO));
       args.add(new SqlIntervalQualifier(TimeUnit.ISOYEAR, null, 
SqlParserPos.ZERO));
       final SqlNode sqlNode = SqlStdOperatorTable.FLOOR.createCall(args);
-      ParseException e = Assert.assertThrows(
-          ParseException.class,
-          () -> 
DruidSqlParserUtils.convertSqlNodeToGranularityThrowingParseExceptions(sqlNode)
-      );
-      Assert.assertEquals("ISOYEAR is not a valid granularity for ingestion", 
e.getMessage());
+      DruidExceptionMatcher
+          .invalidSqlInput()
+          .expectMessageIs(
+              "ISOYEAR is not a valid period granularity for ingestion."
+          )
+          .assertThrowsAndMatches(() -> 
DruidSqlParserUtils.convertSqlNodeToGranularity(sqlNode));
     }
 
     /**
@@ -440,11 +445,12 @@ public class DruidSqlParserUtilsTest
       args.add(new SqlIdentifier("__time", SqlParserPos.ZERO));
       args.add(SqlLiteral.createCharString("abc", SqlParserPos.ZERO));
       final SqlNode sqlNode = 
TimeFloorOperatorConversion.SQL_FUNCTION.createCall(args);
-      DruidException e = Assert.assertThrows(
-          DruidException.class,
-          () -> 
DruidSqlParserUtils.convertSqlNodeToGranularityThrowingParseExceptions(sqlNode)
-      );
-      Assert.assertEquals("granularity['abc'] is an invalid period string", 
e.getMessage());
+      DruidExceptionMatcher
+          .invalidSqlInput()
+          .expectMessageIs(
+              "granularity['abc'] is an invalid period literal."
+          )
+          .assertThrowsAndMatches(() -> 
DruidSqlParserUtils.convertSqlNodeToGranularity(sqlNode));
     }
   }
 


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


Reply via email to