You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pt...@apache.org on 2022/07/29 13:02:00 UTC

[ignite-3] branch main updated: IGNITE-17433 Fix SQL exceptions when cursor is closed or has no more pages (#958)

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

ptupitsyn 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 b7c793f78 IGNITE-17433 Fix SQL exceptions when cursor is closed or has no more pages (#958)
b7c793f78 is described below

commit b7c793f780968d2e01012544b609b296e684cadf
Author: Pavel Tupitsyn <pt...@apache.org>
AuthorDate: Fri Jul 29 16:01:55 2022 +0300

    IGNITE-17433 Fix SQL exceptions when cursor is closed or has no more pages (#958)
    
    * Remove internal `SessionNotFoundException` - use the error code for internal logic.
    * Move `CursorClosedException` to public API.
    * Fix thrown exceptions on server and client.
    * Add shared client/server tests to ensure consistent behavior.
---
 .../internal/lang/IgniteExceptionUtils.java}       | 17 ++++++++---
 .../apache/ignite/internal/sql/ResultSetImpl.java  |  8 ++----
 .../apache/ignite/sql/CursorClosedException.java}  | 33 ++++++++++------------
 .../ignite/sql/NoRowSetExpectedException.java      | 22 +++++++++++----
 .../java/org/apache/ignite/sql/SqlException.java   |  9 ++++++
 .../internal/client/sql/ClientAsyncResultSet.java  | 14 ++++-----
 .../java/org/apache/ignite/lang/ErrorGroups.java   |  3 ++
 .../internal/sql/api/ItSqlAsynchronousApiTest.java | 30 ++++++++++++++++----
 .../internal/sql/api/ItSqlSynchronousApiTest.java  | 27 ++++++++++++++++--
 .../internal/sql/api/AsyncResultSetImpl.java       |  4 ++-
 .../ignite/internal/sql/api/SessionImpl.java       |  4 +--
 .../ignite/internal/sql/engine/AsyncCursor.java    |  2 +-
 .../internal/sql/engine/SqlQueryProcessor.java     |  5 ++--
 .../internal/sql/engine/exec/AsyncWrapper.java     |  6 ++--
 .../sql/engine/exec/rel/AsyncRootNode.java         |  4 +--
 .../sql/engine/exec/AsyncWrapperSelfTest.java      |  9 +++---
 16 files changed, 134 insertions(+), 63 deletions(-)

diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/ClosedCursorException.java b/modules/api/src/main/java/org/apache/ignite/internal/lang/IgniteExceptionUtils.java
similarity index 62%
rename from modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/ClosedCursorException.java
rename to modules/api/src/main/java/org/apache/ignite/internal/lang/IgniteExceptionUtils.java
index 97010c2a4..e77cde70a 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/ClosedCursorException.java
+++ b/modules/api/src/main/java/org/apache/ignite/internal/lang/IgniteExceptionUtils.java
@@ -15,12 +15,21 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.sql.engine;
+package org.apache.ignite.internal.lang;
 
-import org.apache.ignite.lang.IgniteInternalCheckedException;
+import org.apache.ignite.lang.IgniteException;
 
 /**
- * The exception is used to complete result stage of {@link AsyncCursor#requestNextAsync(int)} invocation when invoked on a closed cursor.
+ * Ignite exception utils.
  */
-public class ClosedCursorException extends IgniteInternalCheckedException {
+public final class IgniteExceptionUtils {
+    /**
+     * Gets the Ignite error code if the specified throwable is an {@link IgniteException}.
+     *
+     * @param t Throwable.
+     * @return Ignite error code or null.
+     */
+    public static Integer getIgniteErrorCode(Throwable t) {
+        return (t instanceof IgniteException) ? ((IgniteException) t).code() : null;
+    }
 }
diff --git a/modules/api/src/main/java/org/apache/ignite/internal/sql/ResultSetImpl.java b/modules/api/src/main/java/org/apache/ignite/internal/sql/ResultSetImpl.java
index db7e329d5..281ac63ad 100644
--- a/modules/api/src/main/java/org/apache/ignite/internal/sql/ResultSetImpl.java
+++ b/modules/api/src/main/java/org/apache/ignite/internal/sql/ResultSetImpl.java
@@ -17,14 +17,12 @@
 
 package org.apache.ignite.internal.sql;
 
-import static org.apache.ignite.lang.ErrorGroups.Sql.QUERY_NO_RESULT_SET_ERR;
-
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 import java.util.concurrent.CompletionStage;
+import org.apache.ignite.sql.NoRowSetExpectedException;
 import org.apache.ignite.sql.ResultSet;
 import org.apache.ignite.sql.ResultSetMetadata;
-import org.apache.ignite.sql.SqlException;
 import org.apache.ignite.sql.SqlRow;
 import org.apache.ignite.sql.async.AsyncResultSet;
 import org.jetbrains.annotations.Nullable;
@@ -85,7 +83,7 @@ public class ResultSetImpl implements ResultSet {
     @Override
     public boolean hasNext() {
         if (it == null) {
-            throw new SqlException(QUERY_NO_RESULT_SET_ERR, "There are no results");
+            throw new NoRowSetExpectedException();
         }
 
         return it.hasNext();
@@ -95,7 +93,7 @@ public class ResultSetImpl implements ResultSet {
     @Override
     public SqlRow next() {
         if (it == null) {
-            throw new SqlException(QUERY_NO_RESULT_SET_ERR, "There are no results");
+            throw new NoRowSetExpectedException();
         }
 
         return it.next();
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/session/SessionNotFoundException.java b/modules/api/src/main/java/org/apache/ignite/sql/CursorClosedException.java
similarity index 50%
rename from modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/session/SessionNotFoundException.java
rename to modules/api/src/main/java/org/apache/ignite/sql/CursorClosedException.java
index 3f58c0622..8e1e46d84 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/session/SessionNotFoundException.java
+++ b/modules/api/src/main/java/org/apache/ignite/sql/CursorClosedException.java
@@ -15,36 +15,33 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.sql.engine.session;
+package org.apache.ignite.sql;
 
-import static org.apache.ignite.lang.ErrorGroups.Sql.SESSION_NOT_FOUND_ERR;
-import static org.apache.ignite.lang.IgniteStringFormatter.format;
+import static org.apache.ignite.lang.ErrorGroups.Sql.CURSOR_CLOSED_ERR;
 
-import org.apache.ignite.sql.SqlException;
+import java.util.UUID;
 
 /**
- * Exception is thrown when someone tries to perform action on behalf of a session that has already expired or never exists.
+ * Exception is thrown when query cursor data fetch attempt is performed on a closed cursor.
  */
-public class SessionNotFoundException extends SqlException {
-    private final SessionId sessionId;
-
+public class CursorClosedException extends SqlException {
     /**
-     * Constructor.
+     * Creates a new exception instance.
      *
-     * @param sessionId A session id.
      */
-    public SessionNotFoundException(SessionId sessionId) {
-        super(SESSION_NOT_FOUND_ERR, format("Session not found [{}]", sessionId));
-
-        this.sessionId = sessionId;
+    public CursorClosedException() {
+        super(CURSOR_CLOSED_ERR);
     }
 
     /**
-     * Returns a sessionId of session which was not found.
+     * Creates a new exception with the given trace id, error code, detail message and cause.
      *
-     * @return A session id.
+     * @param traceId Unique identifier of this exception.
+     * @param code Full error code.
+     * @param message Detail message.
+     * @param cause Optional nested exception (can be {@code null}).
      */
-    public SessionId sessionId() {
-        return sessionId;
+    public CursorClosedException(UUID traceId, int code, String message, Throwable cause) {
+        super(traceId, code, message, cause);
     }
 }
diff --git a/modules/api/src/main/java/org/apache/ignite/sql/NoRowSetExpectedException.java b/modules/api/src/main/java/org/apache/ignite/sql/NoRowSetExpectedException.java
index 45bae3236..6813b1281 100644
--- a/modules/api/src/main/java/org/apache/ignite/sql/NoRowSetExpectedException.java
+++ b/modules/api/src/main/java/org/apache/ignite/sql/NoRowSetExpectedException.java
@@ -17,18 +17,30 @@
 
 package org.apache.ignite.sql;
 
-import org.apache.ignite.lang.IgniteException;
+import static org.apache.ignite.lang.ErrorGroups.Sql.QUERY_NO_RESULT_SET_ERR;
+
+import java.util.UUID;
 
 /**
  * Exception is thrown when the query doesn't intend to return any rows. E.g. DML or DDL queries.
  */
-public class NoRowSetExpectedException extends IgniteException {
+public class NoRowSetExpectedException extends SqlException {
     /**
      * Creates an exception.
+     */
+    public NoRowSetExpectedException() {
+        super(QUERY_NO_RESULT_SET_ERR, "Query has no result set");
+    }
+
+    /**
+     * Creates a new exception with the given trace id, error code, detail message and cause.
      *
-     * @param msg Message.
+     * @param traceId Unique identifier of this exception.
+     * @param code Full error code.
+     * @param message Detail message.
+     * @param cause Optional nested exception (can be {@code null}).
      */
-    public NoRowSetExpectedException(String msg) {
-        super(msg);
+    public NoRowSetExpectedException(UUID traceId, int code, String message, Throwable cause) {
+        super(traceId, code, message, cause);
     }
 }
diff --git a/modules/api/src/main/java/org/apache/ignite/sql/SqlException.java b/modules/api/src/main/java/org/apache/ignite/sql/SqlException.java
index 54016e24d..c28e3a0c3 100644
--- a/modules/api/src/main/java/org/apache/ignite/sql/SqlException.java
+++ b/modules/api/src/main/java/org/apache/ignite/sql/SqlException.java
@@ -24,6 +24,15 @@ import org.apache.ignite.lang.IgniteException;
  * SQL exception base class.
  */
 public class SqlException extends IgniteException {
+    /**
+     * Creates a new exception with the given error code.
+     *
+     * @param code Full error code.
+     */
+    public SqlException(int code) {
+        super(code);
+    }
+
     /**
      * Creates a new exception with the given error code and detail message.
      *
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientAsyncResultSet.java b/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientAsyncResultSet.java
index ffcca9bf8..5a1280ee7 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientAsyncResultSet.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientAsyncResultSet.java
@@ -17,7 +17,7 @@
 
 package org.apache.ignite.internal.client.sql;
 
-import static org.apache.ignite.lang.ErrorGroups.Common.UNKNOWN_ERR;
+import static org.apache.ignite.lang.ErrorGroups.Sql.CURSOR_NO_MORE_PAGES_ERR;
 
 import java.time.Duration;
 import java.time.Period;
@@ -29,10 +29,11 @@ import java.util.concurrent.CompletionStage;
 import org.apache.ignite.internal.client.ClientChannel;
 import org.apache.ignite.internal.client.proto.ClientMessageUnpacker;
 import org.apache.ignite.internal.client.proto.ClientOp;
-import org.apache.ignite.lang.IgniteException;
+import org.apache.ignite.sql.CursorClosedException;
 import org.apache.ignite.sql.NoRowSetExpectedException;
 import org.apache.ignite.sql.ResultSetMetadata;
 import org.apache.ignite.sql.SqlColumnType;
+import org.apache.ignite.sql.SqlException;
 import org.apache.ignite.sql.SqlRow;
 import org.apache.ignite.sql.async.AsyncResultSet;
 import org.jetbrains.annotations.Nullable;
@@ -136,13 +137,12 @@ class ClientAsyncResultSet implements AsyncResultSet {
         requireResultSet();
 
         if (closed) {
-            // TODO IGNITE-17135 - same error code and message as on server.
-            return CompletableFuture.failedFuture(new IgniteException(UNKNOWN_ERR, "Cursor is closed."));
+            return CompletableFuture.failedFuture(new CursorClosedException());
         }
 
         if (!hasMorePages()) {
-            // TODO IGNITE-17135 - same error code and message as on server.
-            return CompletableFuture.failedFuture(new IgniteException(UNKNOWN_ERR, "No more pages."));
+            return CompletableFuture.failedFuture(
+                    new SqlException(CURSOR_NO_MORE_PAGES_ERR, "There are no more pages."));
         }
 
         return ch.serviceAsync(
@@ -181,7 +181,7 @@ class ClientAsyncResultSet implements AsyncResultSet {
 
     private void requireResultSet() {
         if (!hasRowSet()) {
-            throw new NoRowSetExpectedException("Query has no result set");
+            throw new NoRowSetExpectedException();
         }
     }
 
diff --git a/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java b/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java
index 0bd9277e2..ab765cbd1 100755
--- a/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java
+++ b/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java
@@ -104,5 +104,8 @@ public class ErrorGroups {
 
         /** Storage engine not valid. */
         public static final int STORAGE_ENGINE_NOT_VALID_ERR = SQL_ERR_GROUP.registerErrorCode(8);
+
+        /** Cursor is already closed error. */
+        public static final int CURSOR_CLOSED_ERR = SQL_ERR_GROUP.registerErrorCode(9);
     }
 }
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlAsynchronousApiTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlAsynchronousApiTest.java
index 05970d5a3..2f3a7b002 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlAsynchronousApiTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlAsynchronousApiTest.java
@@ -49,7 +49,6 @@ import org.apache.ignite.internal.app.IgniteImpl;
 import org.apache.ignite.internal.client.sql.ClientSql;
 import org.apache.ignite.internal.sql.api.ColumnMetadataImpl.ColumnOriginImpl;
 import org.apache.ignite.internal.sql.engine.AbstractBasicIntegrationTest;
-import org.apache.ignite.internal.sql.engine.ClosedCursorException;
 import org.apache.ignite.internal.testframework.IgniteTestUtils;
 import org.apache.ignite.internal.tx.TxManager;
 import org.apache.ignite.internal.tx.TxState;
@@ -64,7 +63,9 @@ import org.apache.ignite.lang.TableAlreadyExistsException;
 import org.apache.ignite.lang.TableNotFoundException;
 import org.apache.ignite.sql.BatchedArguments;
 import org.apache.ignite.sql.ColumnMetadata;
+import org.apache.ignite.sql.CursorClosedException;
 import org.apache.ignite.sql.IgniteSql;
+import org.apache.ignite.sql.NoRowSetExpectedException;
 import org.apache.ignite.sql.ResultSetMetadata;
 import org.apache.ignite.sql.Session;
 import org.apache.ignite.sql.SqlBatchException;
@@ -461,6 +462,11 @@ public class ItSqlAsynchronousApiTest extends AbstractBasicIntegrationTest {
 
     @Test
     public void errors() {
+        sql("CREATE TABLE TEST(ID INT PRIMARY KEY, VAL0 INT)");
+        for (int i = 0; i < ROW_COUNT; ++i) {
+            sql("INSERT INTO TEST VALUES (?, ?)", i, i);
+        }
+
         IgniteSql sql = igniteSql();
         Session ses = sql.sessionBuilder().defaultPageSize(ROW_COUNT / 2).build();
 
@@ -478,8 +484,8 @@ public class ItSqlAsynchronousApiTest extends AbstractBasicIntegrationTest {
 
         // Planning error.
         {
-            CompletableFuture<AsyncResultSet> f = ses.executeAsync(null, "CREATE TABLE TEST (VAL INT)");
-            assertThrowsWithCause(f::get, IgniteException.class, "Table without PRIMARY KEY is not supported");
+            CompletableFuture<AsyncResultSet> f = ses.executeAsync(null, "CREATE TABLE TEST2 (VAL INT)");
+            assertThrowsWithCause(f::get, SqlException.class, "Table without PRIMARY KEY is not supported");
         }
 
         // Execute error.
@@ -487,6 +493,20 @@ public class ItSqlAsynchronousApiTest extends AbstractBasicIntegrationTest {
             CompletableFuture<AsyncResultSet> f = ses.executeAsync(null, "SELECT 1 / ?", 0);
             assertThrowsWithCause(f::get, IgniteException.class, "/ by zero");
         }
+
+        // No result set error.
+        {
+            AsyncResultSet ars = ses.executeAsync(null, "CREATE TABLE TEST3 (ID INT PRIMARY KEY)").join();
+            assertThrowsWithCause(() -> ars.fetchNextPage().toCompletableFuture().get(), NoRowSetExpectedException.class,
+                    "Query has no result set");
+        }
+
+        // Cursor closed error.
+        {
+            AsyncResultSet ars = ses.executeAsync(null, "SELECT * FROM TEST").join();
+            ars.closeAsync().toCompletableFuture().join();
+            assertThrowsWithCause(() -> ars.fetchNextPage().toCompletableFuture().get(), CursorClosedException.class);
+        }
     }
 
     @Test
@@ -503,12 +523,12 @@ public class ItSqlAsynchronousApiTest extends AbstractBasicIntegrationTest {
 
         ses.closeAsync().get();
 
-        // Fetched page  is available after cancel.
+        // Fetched page is available after cancel.
         ars0.currentPage();
 
         assertThrowsWithCause(
                 () -> ars0.fetchNextPage().toCompletableFuture().get(),
-                ClosedCursorException.class
+                SqlException.class
         );
 
         assertThrowsWithCause(
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlSynchronousApiTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlSynchronousApiTest.java
index 91955b850..d9ce59d95 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlSynchronousApiTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlSynchronousApiTest.java
@@ -48,7 +48,9 @@ import org.apache.ignite.lang.IndexAlreadyExistsException;
 import org.apache.ignite.lang.TableAlreadyExistsException;
 import org.apache.ignite.lang.TableNotFoundException;
 import org.apache.ignite.sql.BatchedArguments;
+import org.apache.ignite.sql.CursorClosedException;
 import org.apache.ignite.sql.IgniteSql;
+import org.apache.ignite.sql.NoRowSetExpectedException;
 import org.apache.ignite.sql.ResultSet;
 import org.apache.ignite.sql.Session;
 import org.apache.ignite.sql.SqlBatchException;
@@ -211,9 +213,14 @@ public class ItSqlSynchronousApiTest extends AbstractBasicIntegrationTest {
     }
 
     @Test
-    public void errors() {
+    public void errors() throws InterruptedException {
+        sql("CREATE TABLE TEST(ID INT PRIMARY KEY, VAL0 INT)");
+        for (int i = 0; i < ROW_COUNT; ++i) {
+            sql("INSERT INTO TEST VALUES (?, ?)", i, i);
+        }
+
         IgniteSql sql = igniteSql();
-        Session ses = sql.sessionBuilder().defaultPageSize(ROW_COUNT / 2).build();
+        Session ses = sql.sessionBuilder().defaultPageSize(2).build();
 
         // Parse error.
         assertThrowsWithCause(
@@ -231,7 +238,7 @@ public class ItSqlSynchronousApiTest extends AbstractBasicIntegrationTest {
 
         // Planning error.
         assertThrowsWithCause(
-                () -> ses.execute(null, "CREATE TABLE TEST (VAL INT)"),
+                () -> ses.execute(null, "CREATE TABLE TEST2 (VAL INT)"),
                 SqlException.class,
                 "Table without PRIMARY KEY is not supported"
         );
@@ -242,6 +249,20 @@ public class ItSqlSynchronousApiTest extends AbstractBasicIntegrationTest {
                 IgniteException.class,
                 "/ by zero"
         );
+
+        // No result set error.
+        {
+            ResultSet rs = ses.execute(null, "CREATE TABLE TEST3 (ID INT PRIMARY KEY)");
+            assertThrowsWithCause(rs::next, NoRowSetExpectedException.class, "Query has no result set");
+        }
+
+        // Cursor closed error.
+        {
+            ResultSet rs = ses.execute(null, "SELECT * FROM TEST");
+            Thread.sleep(300); // ResultSetImpl fetches next page in background, wait to it to complete to avoid flakiness.
+            rs.close();
+            assertThrowsWithCause(() -> rs.forEachRemaining(Object::hashCode), CursorClosedException.class);
+        }
     }
 
     @Test
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/AsyncResultSetImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/AsyncResultSetImpl.java
index 3f1acfd9f..ce2e0fcc6 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/AsyncResultSetImpl.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/AsyncResultSetImpl.java
@@ -134,6 +134,8 @@ public class AsyncResultSetImpl implements AsyncResultSet {
     /** {@inheritDoc} */
     @Override
     public CompletionStage<? extends AsyncResultSet> fetchNextPage() {
+        requireResultSet();
+
         if (!hasMorePages()) {
             return HAS_NO_MORE_PAGE_FUTURE;
         } else {
@@ -160,7 +162,7 @@ public class AsyncResultSetImpl implements AsyncResultSet {
 
     private void requireResultSet() {
         if (!hasRowSet()) {
-            throw new NoRowSetExpectedException("Query has no result set: [type=" + cur.queryType() + ']');
+            throw new NoRowSetExpectedException();
         }
     }
 
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/SessionImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/SessionImpl.java
index 9cbbd0428..b0ef5cd22 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/SessionImpl.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/SessionImpl.java
@@ -32,6 +32,7 @@ import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Flow.Publisher;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.ignite.internal.lang.IgniteExceptionUtils;
 import org.apache.ignite.internal.sql.engine.AsyncCursor;
 import org.apache.ignite.internal.sql.engine.QueryContext;
 import org.apache.ignite.internal.sql.engine.QueryProcessor;
@@ -40,7 +41,6 @@ import org.apache.ignite.internal.sql.engine.QueryValidator;
 import org.apache.ignite.internal.sql.engine.prepare.QueryPlan.Type;
 import org.apache.ignite.internal.sql.engine.property.PropertiesHolder;
 import org.apache.ignite.internal.sql.engine.session.SessionId;
-import org.apache.ignite.internal.sql.engine.session.SessionNotFoundException;
 import org.apache.ignite.internal.util.ArrayUtils;
 import org.apache.ignite.internal.util.ExceptionUtils;
 import org.apache.ignite.internal.util.IgniteSpinBusyLock;
@@ -163,7 +163,7 @@ public class SessionImpl implements Session {
             );
 
             result.whenComplete((rs, th) -> {
-                if (th instanceof SessionNotFoundException) {
+                if (IgniteExceptionUtils.getIgniteErrorCode(th) == SESSION_NOT_FOUND_ERR) {
                     closeInternal();
                 }
             });
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/AsyncCursor.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/AsyncCursor.java
index fdeadf2dd..41bdb057f 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/AsyncCursor.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/AsyncCursor.java
@@ -32,7 +32,7 @@ public interface AsyncCursor<T> {
      *
      * <p>Several calls to this method should be chained and resulting stages should be completed in the order of invocation. Any call
      * to this method after call to {@link #closeAsync()} should be completed immediately with
-     * {@link org.apache.ignite.internal.sql.engine.ClosedCursorException} even if the future returned by {@link #closeAsync()}
+     * {@link org.apache.ignite.sql.CursorClosedException} even if the future returned by {@link #closeAsync()}
      * is not completed yet.
      *
      * @param rows Desired amount of rows.
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlQueryProcessor.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlQueryProcessor.java
index 7de73d005..1e81423c7 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlQueryProcessor.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlQueryProcessor.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.sql.engine;
 
 import static org.apache.ignite.internal.sql.engine.util.Commons.FRAMEWORK_CONFIG;
 import static org.apache.ignite.lang.ErrorGroups.Sql.QUERY_INVALID_ERR;
+import static org.apache.ignite.lang.ErrorGroups.Sql.SESSION_NOT_FOUND_ERR;
 import static org.apache.ignite.lang.IgniteStringFormatter.format;
 
 import java.util.ArrayList;
@@ -59,7 +60,6 @@ import org.apache.ignite.internal.sql.engine.schema.SqlSchemaManager;
 import org.apache.ignite.internal.sql.engine.schema.SqlSchemaManagerImpl;
 import org.apache.ignite.internal.sql.engine.session.SessionId;
 import org.apache.ignite.internal.sql.engine.session.SessionManager;
-import org.apache.ignite.internal.sql.engine.session.SessionNotFoundException;
 import org.apache.ignite.internal.sql.engine.util.BaseQueryContext;
 import org.apache.ignite.internal.sql.engine.util.Commons;
 import org.apache.ignite.internal.storage.DataStorageManager;
@@ -292,7 +292,8 @@ public class SqlQueryProcessor implements QueryProcessor {
         var session = sessionManager.session(sessionId);
 
         if (session == null) {
-            return CompletableFuture.failedFuture(new SessionNotFoundException(sessionId));
+            return CompletableFuture.failedFuture(
+                    new SqlException(SESSION_NOT_FOUND_ERR, format("Session not found [{}]", sessionId)));
         }
 
         var schemaName = session.queryProperties().get(QueryProperty.DEFAULT_SCHEMA);
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/AsyncWrapper.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/AsyncWrapper.java
index fa9a23a68..7e9918f26 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/AsyncWrapper.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/AsyncWrapper.java
@@ -26,7 +26,7 @@ import java.util.concurrent.CompletionStage;
 import java.util.concurrent.Executor;
 import java.util.function.Function;
 import org.apache.ignite.internal.sql.engine.AsyncCursor;
-import org.apache.ignite.internal.sql.engine.ClosedCursorException;
+import org.apache.ignite.sql.CursorClosedException;
 
 /**
  * Wrapper that converts a synchronous iterator to an asynchronous one.
@@ -82,7 +82,7 @@ public class AsyncWrapper<T> implements AsyncCursor<T> {
 
         synchronized (lock) {
             if (cancelled) {
-                next.completeExceptionally(new ClosedCursorException());
+                next.completeExceptionally(new CursorClosedException());
 
                 return next;
             }
@@ -124,7 +124,7 @@ public class AsyncWrapper<T> implements AsyncCursor<T> {
         if (!cancelled) {
             synchronized (lock) {
                 if (!cancelled) {
-                    requestChainTail.completeExceptionally(new ClosedCursorException());
+                    requestChainTail.completeExceptionally(new CursorClosedException());
 
                     cursorFut.thenAcceptAsync(cursor -> {
                         if (cursor instanceof AutoCloseable) {
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/AsyncRootNode.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/AsyncRootNode.java
index 56663fa0f..5a2c4744b 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/AsyncRootNode.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/AsyncRootNode.java
@@ -27,8 +27,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Function;
 import org.apache.ignite.internal.sql.engine.AsyncCursor;
-import org.apache.ignite.internal.sql.engine.ClosedCursorException;
 import org.apache.ignite.internal.sql.engine.exec.ExecutionCancelledException;
+import org.apache.ignite.sql.CursorClosedException;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -164,7 +164,7 @@ public class AsyncRootNode<InRowT, OutRowT> implements Downstream<InRowT>, Async
 
         synchronized (lock) {
             if (closed) {
-                next.completeExceptionally(new ClosedCursorException());
+                next.completeExceptionally(new CursorClosedException());
 
                 return next;
             }
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/AsyncWrapperSelfTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/AsyncWrapperSelfTest.java
index f44e718a3..ec75cec3f 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/AsyncWrapperSelfTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/AsyncWrapperSelfTest.java
@@ -33,7 +33,7 @@ import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionException;
 import java.util.concurrent.ForkJoinPool;
 import org.apache.ignite.internal.sql.engine.AsyncCursor;
-import org.apache.ignite.internal.sql.engine.ClosedCursorException;
+import org.apache.ignite.sql.SqlException;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 
@@ -99,7 +99,6 @@ public class AsyncWrapperSelfTest {
      * Call to {@link AsyncCursor#closeAsync()} should be passed to delegate in case the latter implements {@link AutoCloseable}.
      */
     @Test
-    @SuppressWarnings("unchecked")
     public void testClosePropagatedToDelegate() throws Exception {
         var mockIt = (ClosableIterator<Object>) Mockito.mock(ClosableIterator.class);
         var cursor = new AsyncWrapper<>(mockIt);
@@ -153,14 +152,14 @@ public class AsyncWrapperSelfTest {
                 .thenAccept(batch -> assertThat(batch.items(), equalTo(data.subList(0, 1))))
                 .exceptionally(ex -> {
                     assertInstanceOf(CompletionException.class, ex);
-                    assertInstanceOf(ClosedCursorException.class, ex.getCause());
+                    assertInstanceOf(SqlException.class, ex.getCause());
 
                     return null;
                 });
         var stage2 = cursor.closeAsync();
         var stage3 = cursor.requestNextAsync(1)
                 .exceptionally(ex -> {
-                    assertInstanceOf(ClosedCursorException.class, ex);
+                    assertInstanceOf(SqlException.class, ex);
 
                     return null;
                 });
@@ -187,4 +186,4 @@ public class AsyncWrapperSelfTest {
     private interface ClosableIterator<T> extends Iterator<T>, AutoCloseable {
 
     }
-}
\ No newline at end of file
+}