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

jooger pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 6f940a66ee6 IGNITE-24452 Sql. Assertion for unsupported Calcite 
statements (#5288)
6f940a66ee6 is described below

commit 6f940a66ee6f937d63b4577ac8352eb8d1c79ff6
Author: Max Zhuravkov <[email protected]>
AuthorDate: Fri Feb 28 10:58:38 2025 +0200

    IGNITE-24452 Sql. Assertion for unsupported Calcite statements (#5288)
---
 .../internal/sql/engine/sql/IgniteSqlParser.java   | 23 +++++++++++++-
 .../ignite/internal/sql/engine/util/Commons.java   | 14 ++++++++-
 .../internal/sql/engine/util/IgniteResource.java   |  4 +++
 .../sql/engine/sql/IgniteSqlParserSelfTest.java    | 36 ++++++++++++++++++++++
 4 files changed, 75 insertions(+), 2 deletions(-)

diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlParser.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlParser.java
index 2fe97d27c19..7f06d2db7cc 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlParser.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlParser.java
@@ -48,6 +48,8 @@ import 
org.apache.ignite.internal.generated.query.calcite.sql.IgniteSqlParserImp
 import org.apache.ignite.internal.generated.query.calcite.sql.ParseException;
 import org.apache.ignite.internal.generated.query.calcite.sql.Token;
 import org.apache.ignite.internal.generated.query.calcite.sql.TokenMgrError;
+import org.apache.ignite.internal.sql.engine.util.Commons;
+import org.apache.ignite.internal.sql.engine.util.IgniteResource;
 import org.apache.ignite.internal.util.StringUtils;
 import org.apache.ignite.sql.SqlException;
 import org.checkerframework.checker.nullness.qual.Nullable;
@@ -118,6 +120,9 @@ public final class IgniteSqlParser  {
             for (int i = 0; i < list.size(); i++) {
                 SqlNode original = list.get(i);
                 SqlNode node = fixNodesIfNecessary(original);
+
+                validateTopLevelNode(node);
+
                 list.set(i, node);
             }
 
@@ -132,6 +137,22 @@ public final class IgniteSqlParser  {
         }
     }
 
+    private static void validateTopLevelNode(SqlNode node) {
+        boolean knownType = Commons.getQueryType(node) != null;
+
+        if (!knownType) {
+            String sqlString = node.toString();
+            // Extract first two tokens of a statement for an error message:
+            // DESCRIBE TABLE t -> DESCRIBE TABLE
+            // UPSERT INTO t (a, b) -> UPSERT INTO
+            int index1 = sqlString.indexOf(' ');
+            int index2 = index1 > 0 ? sqlString.indexOf(' ', index1 + 1) : 
index1;
+            String sql = sqlString.substring(0, index2);
+
+            throw SqlUtil.newContextException(node.getParserPosition(), 
IgniteResource.INSTANCE.unexpectedStatement(sql));
+        }
+    }
+
     /**
      * Converts the given exception to the {@link SqlException}.
      *
@@ -217,7 +238,7 @@ public final class IgniteSqlParser  {
             );
         }
 
-        return new SqlException(STMT_PARSE_ERR, "Failed to parse query: " + 
message);
+        return new SqlException(STMT_PARSE_ERR, "Failed to parse query: " + 
message, ex);
     }
 
     private static final class InternalIgniteSqlParser extends 
IgniteSqlParserImpl {
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
index a316bb92489..ae0802b7160 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
@@ -43,6 +43,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -69,6 +70,7 @@ import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.SqlInsert;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.type.SqlTypeCoercionRule;
@@ -136,6 +138,13 @@ public final class Commons {
     public static final int IO_BATCH_SIZE = 256;
     public static final int IO_BATCH_COUNT = 4;
 
+    private static final EnumSet<SqlKind> SUPPORTED_DDL = EnumSet.of(
+            SqlKind.CREATE_SCHEMA, SqlKind.DROP_SCHEMA,
+            SqlKind.CREATE_TABLE, SqlKind.ALTER_TABLE, SqlKind.DROP_TABLE,
+            SqlKind.CREATE_INDEX, SqlKind.ALTER_INDEX, SqlKind.DROP_INDEX,
+            SqlKind.OTHER_DDL
+    );
+
     /**
      * The number of elements to be prefetched from each partition when 
scanning the sorted index.
      * The higher the value, the fewer calls to the upstream will be, but at 
the same time, the bigger
@@ -828,7 +837,7 @@ public final class Commons {
             return SqlQueryType.TX_CONTROL;
         }
 
-        if (SqlKind.DDL.contains(sqlKind)) {
+        if (SUPPORTED_DDL.contains(sqlKind)) {
             return SqlQueryType.DDL;
         }
 
@@ -847,6 +856,9 @@ public final class Commons {
                 return SqlQueryType.QUERY;
 
             case INSERT:
+                assert sqlNode instanceof SqlInsert;
+                SqlInsert insert = (SqlInsert) sqlNode;
+                return insert.isUpsert() ? null : SqlQueryType.DML;
             case DELETE:
             case UPDATE:
             case MERGE:
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteResource.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteResource.java
index 1dd78ca6c21..fc409b31e36 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteResource.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteResource.java
@@ -23,6 +23,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.runtime.CalciteException;
 import org.apache.calcite.runtime.Resources;
 import org.apache.calcite.runtime.Resources.BaseMessage;
 import org.apache.calcite.runtime.Resources.ExInst;
@@ -104,6 +105,9 @@ public interface IgniteResource {
     @BaseMessage("A recursive query is not supported.")
     ExInst<SqlValidatorException> recursiveQueryIsNotSupported();
 
+    @BaseMessage("Unexpected statement: {0} ")
+    ExInst<CalciteException> unexpectedStatement(String type);
+
     /** Constructs a signature string to use in error messages. */
     static String makeSignature(SqlCallBinding binding, RelDataType... 
operandTypes) {
         return makeSignature(binding, Arrays.asList(operandTypes));
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlParserSelfTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlParserSelfTest.java
index a4f6fde83f2..6a139814ee5 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlParserSelfTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlParserSelfTest.java
@@ -18,11 +18,16 @@
 package org.apache.ignite.internal.sql.engine.sql;
 
 import static 
org.apache.ignite.internal.sql.engine.util.SqlTestUtils.assertThrowsSqlException;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.stream.Stream;
+import org.apache.calcite.runtime.CalciteContextException;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.ignite.lang.ErrorGroups.Sql;
@@ -161,6 +166,37 @@ public class IgniteSqlParserSelfTest {
         assertEquals(statement.clone(pos).toString(), statement.toString());
     }
 
+    @ParameterizedTest
+    @MethodSource("unsupportedStatements")
+    public void testRejectUnsupportedStatements(String stmt, String error) {
+        CalciteContextException err1 = 
assertThrows(CalciteContextException.class,
+                () -> IgniteSqlParser.parse(stmt, StatementParseResult.MODE));
+        assertThat(err1.getMessage(), containsString("Unexpected statement: " 
+ error));
+
+        assertThrows(CalciteContextException.class, () -> 
IgniteSqlParser.parse(stmt, ScriptParseResult.MODE));
+    }
+
+    private static Stream<Arguments> unsupportedStatements() {
+        return Stream.of(
+                Arguments.of("ALTER SYSTEM SET identifier = expression", 
"ALTER SYSTEM"),
+                Arguments.of("ALTER SYSTEM RESET identifier", "ALTER SYSTEM"),
+                Arguments.of("ALTER SYSTEM RESET ALL", "ALTER SYSTEM"),
+
+                Arguments.of("ALTER SESSION SET identifier = expression", 
"ALTER SESSION"),
+                Arguments.of("ALTER SESSION RESET identifier", "ALTER 
SESSION"),
+                Arguments.of("ALTER SESSION RESET ALL", "ALTER SESSION"),
+
+                Arguments.of("DESCRIBE DATABASE db", "DESCRIBE SCHEMA"),
+                Arguments.of("DESCRIBE CATALOG cat", "DESCRIBE SCHEMA"),
+                Arguments.of("DESCRIBE SCHEMA db.cat.s", "DESCRIBE SCHEMA"),
+                Arguments.of("DESCRIBE TABLE db.cat.s.t", "DESCRIBE TABLE"),
+                // TODO https://issues.apache.org/jira/browse/IGNITE-24630 
DESCRIBE <statement> is converted to EXPLAIN PLAN FOR
+                // Arguments.of("DESCRIBE STATEMENT SELECT 1", "EXPLAIN PLAN"),
+
+                Arguments.of("UPSERT INTO t (a, b, c) SELECT 1, 2, 3", "UPSERT 
INTO")
+        );
+    }
+
     @ParameterizedTest
     @MethodSource("dmlStatements")
     public void testScriptDmlStatementsClone(String stmt) {

Reply via email to