diff --git a/sql/src/main/codegen/includes/insert.ftl b/sql/src/main/codegen/includes/insert.ftl index 3eac7d19b23..07898aaadc9 100644 --- a/sql/src/main/codegen/includes/insert.ftl +++ b/sql/src/main/codegen/includes/insert.ftl @@ -36,6 +36,11 @@ SqlNode DruidSqlInsertEof() : clusteredBy = ClusterItems() ] + { + if (clusteredBy != null && partitionedBy.lhs == null) { + throw new ParseException("CLUSTERED BY found before PARTITIONED BY. In druid, the CLUSTERED BY clause has to be specified after the PARTITIONED BY clause"); + } + } // EOF is also present in SqlStmtEof but EOF is a special case and a single EOF can be consumed multiple times. // The reason for adding EOF here is to ensure that we create a DruidSqlInsert node after the syntax has been // validated and throw SQL syntax errors before performing validations in the DruidSqlInsert which can overshadow the diff --git a/sql/src/main/codegen/includes/replace.ftl b/sql/src/main/codegen/includes/replace.ftl index 20c9aac9a8c..ed9eee46deb 100644 --- a/sql/src/main/codegen/includes/replace.ftl +++ b/sql/src/main/codegen/includes/replace.ftl @@ -56,6 +56,11 @@ SqlNode DruidSqlReplaceEof() : clusteredBy = ClusterItems() ] + { + if (clusteredBy != null && partitionedBy.lhs == null) { + throw new ParseException("CLUSTERED BY found before PARTITIONED BY. In druid, the CLUSTERED BY clause has to be specified after the PARTITIONED BY clause"); + } + } // EOF is also present in SqlStmtEof but EOF is a special case and a single EOF can be consumed multiple times. // The reason for adding EOF here is to ensure that we create a DruidSqlReplace node after the syntax has been // validated and throw SQL syntax errors before performing validations in the DruidSqlReplace which can overshadow the 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 87c389a722d..e718cc2bdf9 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 @@ -348,6 +348,19 @@ public class CalciteInsertDmlTest extends CalciteIngestionDmlTest .verify(); } + @Test + public void testInsertWithoutPartitionedByWithClusteredBy() + { + testIngestionQuery() + .sql( + "INSERT INTO druid.dst " + + "SELECT __time, FLOOR(m1) as floor_m1, dim1, CEIL(m2) as ceil_m2 FROM foo " + + "CLUSTERED BY 2, dim1 DESC, CEIL(m2)" + ) + .expectValidationError(SqlPlanningException.class, "CLUSTERED BY found before PARTITIONED BY. In druid, the CLUSTERED BY clause has to be specified after the PARTITIONED BY clause") + .verify(); + } + @Test public void testInsertWithPartitionedByAndClusteredBy() { 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 6877ce80422..9c76cabfd88 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 @@ -392,6 +392,24 @@ public class CalciteReplaceDmlTest extends CalciteIngestionDmlTest .verify(); } + @Test + public void testReplaceWithoutPartitionedBy() + { + testIngestionQuery() + .sql("REPLACE INTO dst OVERWRITE ALL SELECT __time, FLOOR(m1) as floor_m1, dim1 FROM foo") + .expectValidationError(SqlPlanningException.class, "REPLACE statements must specify PARTITIONED BY clause explicitly") + .verify(); + } + + @Test + public void testReplaceWithoutPartitionedByWithClusteredBy() + { + testIngestionQuery() + .sql("REPLACE INTO dst OVERWRITE ALL SELECT __time, FLOOR(m1) as floor_m1, dim1 FROM foo CLUSTERED BY dim1") + .expectValidationError(SqlPlanningException.class, "CLUSTERED BY found before PARTITIONED BY. In druid, the CLUSTERED BY clause has to be specified after the PARTITIONED BY clause") + .verify(); + } + @Test public void testReplaceWithoutOverwriteClause() {