diff --git a/pom.xml b/pom.xml index b03e3370599..9640cd48d14 100644 --- a/pom.xml +++ b/pom.xml @@ -84,6 +84,7 @@ 1.11.3 1.35.0 6.2.12 diff --git a/sql/src/main/codegen/includes/insert.ftl b/sql/src/main/codegen/includes/insert.ftl index 0f1db5a4448..00133496d24 100644 --- a/sql/src/main/codegen/includes/insert.ftl +++ b/sql/src/main/codegen/includes/insert.ftl @@ -17,6 +17,63 @@ * under the License. */ +/** + * Parses an INSERT statement. This function is copied from SqlInsert in core/src/main/codegen/templates/Parser.jj, + * with some changes to allow a custom error message if an OVERWRITE clause is present. + */ +SqlNode DruidSqlInsert() : +{ + final List keywords = new ArrayList(); + final SqlNodeList keywordList; + final SqlIdentifier tableName; + SqlNode tableRef; + SqlNode source; + final SqlNodeList columnList; + final Span s; + final Pair p; +} +{ + ( + + | + { keywords.add(SqlInsertKeyword.UPSERT.symbol(getPos())); } + ) + { s = span(); } + SqlInsertKeywords(keywords) { + keywordList = new SqlNodeList(keywords, s.addAll(keywords).pos()); + } + tableName = CompoundTableIdentifier() + ( tableRef = TableHints(tableName) | { tableRef = tableName; } ) + [ LOOKAHEAD(5) tableRef = ExtendTable(tableRef) ] + ( + LOOKAHEAD(2) + p = ParenthesizedCompoundIdentifierList() { + if (p.right.size() > 0) { + tableRef = extend(tableRef, p.right); + } + if (p.left.size() > 0) { + columnList = p.left; + } else { + columnList = null; + } + } + | { columnList = null; } + ) + ( + + { + throw org.apache.druid.sql.calcite.parser.DruidSqlParserUtils.problemParsing( + "An OVERWRITE clause is not allowed with INSERT statements. Use REPLACE statements if overwriting existing segments is required or remove the OVERWRITE clause." + ); + } + | + source = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) { + return new SqlInsert(s.end(source), keywordList, tableRef, source, + columnList); + } + ) +} + // Using fully qualified name for Pair class, since Calcite also has a same class name being used in the Parser.jj SqlNode DruidSqlInsertEof() : { @@ -25,7 +82,7 @@ SqlNode DruidSqlInsertEof() : SqlNodeList clusteredBy = null; } { - insertNode = SqlInsert() + insertNode = DruidSqlInsert() // 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. [ 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 5752ed9e535..a34c93ce9c9 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 @@ -1418,6 +1418,15 @@ public class CalciteInsertDmlTest extends CalciteIngestionDmlTest .verify(); } + @Test + public void testInsertWithOverwriteClause() + { + testIngestionQuery() + .sql("INSERT INTO dst OVERWRITE ALL SELECT * FROM foo PARTITIONED BY ALL TIME") + .expectValidationError(DruidException.class, "An OVERWRITE clause is not allowed with INSERT statements. Use REPLACE statements if overwriting existing segments is required or remove the OVERWRITE clause.") + .verify(); + } + @Test public void testInsertFromExternalProjectSort() {