You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by an...@apache.org on 2024/02/06 05:39:27 UTC
(ignite-3) branch main updated: IGNITE-21146 Error handling in Criteria queries (#2989)
This is an automated email from the ASF dual-hosted git repository.
anovikov 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 ee00a7c028 IGNITE-21146 Error handling in Criteria queries (#2989)
ee00a7c028 is described below
commit ee00a7c028ef6c8c1d8cdab0169fdd36deb1c5fe
Author: Andrey Novikov <an...@apache.org>
AuthorDate: Tue Feb 6 12:39:22 2024 +0700
IGNITE-21146 Error handling in Criteria queries (#2989)
---
.../java/org/apache/ignite/lang/AsyncCursor.java | 2 +-
.../{sql => lang}/CursorClosedException.java | 6 +-
.../java/org/apache/ignite/lang/ErrorGroups.java | 9 +-
.../apache/ignite/sql/async/AsyncResultSet.java | 4 +-
.../ignite/table/criteria/CriteriaException.java | 111 +++++++++++++++++++++
.../ignite/table/criteria/CriteriaQuerySource.java | 9 +-
.../ignite/internal/IgniteExceptionArchTest.java | 2 +-
.../internal/client/sql/ClientAsyncResultSet.java | 11 +-
.../internal/client/table/AbstractClientView.java | 12 ++-
.../criteria/CriteriaExceptionMapperUtil.java} | 31 +++---
.../internal/table/criteria/CursorAdapter.java | 18 ++--
.../table/criteria/QueryCriteriaAsyncCursor.java | 15 ++-
.../apache/ignite/internal/util/AsyncCursor.java | 5 +-
.../apache/ignite/internal/util/AsyncWrapper.java | 2 +-
.../criteria/CriteriaExceptionMapperUtilTest.java | 80 +++++++++++++++
.../ignite/internal/util/AsyncWrapperSelfTest.java | 6 +-
modules/platforms/cpp/ignite/common/error_codes.h | 3 +-
modules/platforms/cpp/ignite/odbc/common_types.cpp | 6 +-
.../platforms/dotnet/Apache.Ignite/ErrorCodes.g.cs | 9 +-
.../dotnet/Apache.Ignite/Internal/Sql/ResultSet.cs | 2 +-
.../ignite/internal/sql/api/ItCommonApiTest.java | 2 +-
.../ignite/internal/sql/api/ItSqlApiBaseTest.java | 21 ++--
.../internal/lang/SqlExceptionMapperUtil.java | 4 +-
.../internal/sql/api/AsyncResultSetImpl.java | 26 ++---
.../sql/engine/exec/rel/AsyncRootNode.java | 2 +-
.../internal/lang/SqlExceptionMapperUtilTest.java | 23 +++--
.../ignite/internal/table/ItCriteriaQueryTest.java | 45 ++++++++-
.../ignite/internal/table/AbstractTableView.java | 14 ++-
28 files changed, 362 insertions(+), 118 deletions(-)
diff --git a/modules/api/src/main/java/org/apache/ignite/lang/AsyncCursor.java b/modules/api/src/main/java/org/apache/ignite/lang/AsyncCursor.java
index 8a7e273538..a21b15b961 100644
--- a/modules/api/src/main/java/org/apache/ignite/lang/AsyncCursor.java
+++ b/modules/api/src/main/java/org/apache/ignite/lang/AsyncCursor.java
@@ -49,7 +49,7 @@ public interface AsyncCursor<T> {
*
* @return A future which will be completed when next page will be fetched and set as the current page.
* The future will return {@code this} for chaining.
- * @throws IgniteException If resource is closed or if there are no more results.
+ * @throws CursorClosedException If cursor is closed.
*/
CompletableFuture<? extends AsyncCursor<T>> fetchNextPage();
diff --git a/modules/api/src/main/java/org/apache/ignite/sql/CursorClosedException.java b/modules/api/src/main/java/org/apache/ignite/lang/CursorClosedException.java
similarity index 86%
rename from modules/api/src/main/java/org/apache/ignite/sql/CursorClosedException.java
rename to modules/api/src/main/java/org/apache/ignite/lang/CursorClosedException.java
index 2bad187d4f..b7a5053b8c 100644
--- a/modules/api/src/main/java/org/apache/ignite/sql/CursorClosedException.java
+++ b/modules/api/src/main/java/org/apache/ignite/lang/CursorClosedException.java
@@ -15,14 +15,14 @@
* limitations under the License.
*/
-package org.apache.ignite.sql;
+package org.apache.ignite.lang;
-import static org.apache.ignite.lang.ErrorGroups.Sql.CURSOR_CLOSED_ERR;
+import static org.apache.ignite.lang.ErrorGroups.Common.CURSOR_CLOSED_ERR;
/**
* Exception is thrown when a data fetch attempt is performed on a closed cursor.
*/
-public class CursorClosedException extends SqlException {
+public class CursorClosedException extends IgniteException {
/**
* Creates an exception instance.
*/
diff --git a/modules/api/src/main/java/org/apache/ignite/lang/ErrorGroups.java b/modules/api/src/main/java/org/apache/ignite/lang/ErrorGroups.java
index e5742d4d0d..01ebc37d01 100755
--- a/modules/api/src/main/java/org/apache/ignite/lang/ErrorGroups.java
+++ b/modules/api/src/main/java/org/apache/ignite/lang/ErrorGroups.java
@@ -133,6 +133,9 @@ public class ErrorGroups {
/** Operation failed because a node has left the cluster. */
public static final int NODE_LEFT_ERR = COMMON_ERR_GROUP.registerErrorCode((short) 5);
+ /** Cursor is already closed error. */
+ public static final int CURSOR_CLOSED_ERR = COMMON_ERR_GROUP.registerErrorCode((short) 6);
+
/**
* This error code represents an internal error caused by faulty logic or coding in the Ignite codebase.
* In general, this error code should be considered as a non-recoverable error
@@ -211,18 +214,12 @@ public class ErrorGroups {
/** SQL error group. */
public static final ErrorGroup SQL_ERR_GROUP = registerGroup("SQL", (short) 4);
- /** No more pages in the cursor error. */
- public static final int CURSOR_NO_MORE_PAGES_ERR = SQL_ERR_GROUP.registerErrorCode((short) 1);
-
/** Query without a result set error. */
public static final int QUERY_NO_RESULT_SET_ERR = SQL_ERR_GROUP.registerErrorCode((short) 2);
/** Schema not found. */
public static final int SCHEMA_NOT_FOUND_ERR = SQL_ERR_GROUP.registerErrorCode((short) 3);
- /** Cursor is already closed error. */
- public static final int CURSOR_CLOSED_ERR = SQL_ERR_GROUP.registerErrorCode((short) 4);
-
/** Statement parsing error. This error is returned when an SQL statement string is not valid according to syntax rules. */
public static final int STMT_PARSE_ERR = SQL_ERR_GROUP.registerErrorCode((short) 5);
diff --git a/modules/api/src/main/java/org/apache/ignite/sql/async/AsyncResultSet.java b/modules/api/src/main/java/org/apache/ignite/sql/async/AsyncResultSet.java
index 4ba91a0822..50a18db1ad 100644
--- a/modules/api/src/main/java/org/apache/ignite/sql/async/AsyncResultSet.java
+++ b/modules/api/src/main/java/org/apache/ignite/sql/async/AsyncResultSet.java
@@ -19,12 +19,11 @@ package org.apache.ignite.sql.async;
import java.util.concurrent.CompletableFuture;
import org.apache.ignite.lang.AsyncCursor;
-import org.apache.ignite.sql.CursorClosedException;
+import org.apache.ignite.lang.CursorClosedException;
import org.apache.ignite.sql.NoRowSetExpectedException;
import org.apache.ignite.sql.ResultSet;
import org.apache.ignite.sql.ResultSetMetadata;
import org.apache.ignite.sql.Session;
-import org.apache.ignite.sql.SqlException;
import org.apache.ignite.sql.SqlRow;
import org.apache.ignite.table.mapper.Mapper;
import org.apache.ignite.tx.Transaction;
@@ -131,7 +130,6 @@ public interface AsyncResultSet<T> extends AsyncCursor<T> {
* The future will return {@code this} for chaining.
* @throws NoRowSetExpectedException If no row set is expected as a query result.
* @throws CursorClosedException If cursor is closed.
- * @throws SqlException If there are no more pages.
*/
@Override
CompletableFuture<? extends AsyncResultSet<T>> fetchNextPage();
diff --git a/modules/api/src/main/java/org/apache/ignite/table/criteria/CriteriaException.java b/modules/api/src/main/java/org/apache/ignite/table/criteria/CriteriaException.java
new file mode 100644
index 0000000000..c51b247bce
--- /dev/null
+++ b/modules/api/src/main/java/org/apache/ignite/table/criteria/CriteriaException.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.table.criteria;
+
+import java.util.UUID;
+import org.apache.ignite.lang.IgniteException;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Criteria exception base class.
+ */
+public class CriteriaException extends IgniteException {
+ /**
+ * Creates an exception with the given error code.
+ *
+ * @param code Full error code.
+ */
+ public CriteriaException(int code) {
+ super(code);
+ }
+
+ /**
+ * Creates an exception with the given trace ID and error code.
+ *
+ * @param traceId Unique identifier of the exception.
+ * @param code Full error code.
+ */
+ public CriteriaException(UUID traceId, int code) {
+ super(traceId, code);
+ }
+
+ /**
+ * Creates an exception with the given error code and detailed message.
+ *
+ * @param code Full error code.
+ * @param message Detailed message.
+ */
+ public CriteriaException(int code, String message) {
+ super(code, message);
+ }
+
+ /**
+ * Creates an exception with the given trace ID, error code, and detailed message.
+ *
+ * @param traceId Unique identifier of this exception.
+ * @param code Full error code.
+ * @param message Detailed message.
+ */
+ public CriteriaException(UUID traceId, int code, String message) {
+ super(traceId, code, message);
+ }
+
+ /**
+ * Creates an exception with the given error code and cause.
+ *
+ * @param code Full error code.
+ * @param cause Optional nested exception (can be {@code null}).
+ */
+ public CriteriaException(int code, Throwable cause) {
+ super(code, cause);
+ }
+
+ /**
+ * Creates an exception with the given trace ID, error code, and cause.
+ *
+ * @param traceId Unique identifier of the exception.
+ * @param code Full error code.
+ * @param cause Optional nested exception (can be {@code null}).
+ */
+ public CriteriaException(UUID traceId, int code, Throwable cause) {
+ super(traceId, code, cause);
+ }
+
+ /**
+ * Creates an exception with the given error code, detailed message, and cause.
+ *
+ * @param code Full error code.
+ * @param message Detailed message.
+ * @param cause Optional nested exception (can be {@code null}).
+ */
+ public CriteriaException(int code, String message, @Nullable Throwable cause) {
+ super(code, message, cause);
+ }
+
+ /**
+ * Creates an exception with the given trace ID, error code, detailed message, and cause.
+ *
+ * @param traceId Unique identifier of the exception.
+ * @param code Full error code.
+ * @param message Detailed message.
+ * @param cause Optional nested exception (can be {@code null}).
+ */
+ public CriteriaException(UUID traceId, int code, String message, @Nullable Throwable cause) {
+ super(traceId, code, message, cause);
+ }
+}
diff --git a/modules/api/src/main/java/org/apache/ignite/table/criteria/CriteriaQuerySource.java b/modules/api/src/main/java/org/apache/ignite/table/criteria/CriteriaQuerySource.java
index bfcf9fd3cc..8d10452c85 100644
--- a/modules/api/src/main/java/org/apache/ignite/table/criteria/CriteriaQuerySource.java
+++ b/modules/api/src/main/java/org/apache/ignite/table/criteria/CriteriaQuerySource.java
@@ -20,7 +20,6 @@ package org.apache.ignite.table.criteria;
import java.util.concurrent.CompletableFuture;
import org.apache.ignite.lang.AsyncCursor;
import org.apache.ignite.lang.Cursor;
-import org.apache.ignite.lang.IgniteException;
import org.apache.ignite.tx.Transaction;
import org.jetbrains.annotations.Nullable;
@@ -36,7 +35,7 @@ public interface CriteriaQuerySource<T> {
* @param tx Transaction to execute the query within or {@code null} to run within implicit transaction.
* @param criteria The predicate to filter entries or {@code null} to return all entries from the underlying table.
* @return Iterator with query results.
- * @throws IgniteException If failed.
+ * @throws CriteriaException If failed.
*/
default Cursor<T> query(@Nullable Transaction tx, @Nullable Criteria criteria) {
return query(tx, criteria, null);
@@ -49,7 +48,7 @@ public interface CriteriaQuerySource<T> {
* @param criteria The predicate to filter entries or {@code null} to return all entries from the underlying table.
* @param opts Criteria query options or {@code null} to use default.
* @return Iterator with query results.
- * @throws IgniteException If failed.
+ * @throws CriteriaException If failed.
*/
Cursor<T> query(@Nullable Transaction tx, @Nullable Criteria criteria, @Nullable CriteriaQueryOptions opts);
@@ -59,7 +58,7 @@ public interface CriteriaQuerySource<T> {
* @param tx Transaction to execute the query within or {@code null} to run within implicit transaction.
* @param criteria The predicate to filter entries or {@code null} to return all entries from the underlying table.
* @return Future that represents the pending completion of the operation.
- * @throws IgniteException If failed.
+ * @throws CriteriaException If failed.
*/
default CompletableFuture<AsyncCursor<T>> queryAsync(@Nullable Transaction tx, @Nullable Criteria criteria) {
return queryAsync(tx, criteria, null);
@@ -72,7 +71,7 @@ public interface CriteriaQuerySource<T> {
* @param criteria The predicate to filter entries or {@code null} to return all entries from the underlying table.
* @param opts Criteria query options or {@code null} to use default.
* @return Future that represents the pending completion of the operation.
- * @throws IgniteException If failed.
+ * @throws CriteriaException If failed.
*/
CompletableFuture<AsyncCursor<T>> queryAsync(
@Nullable Transaction tx,
diff --git a/modules/arch-test/src/test/java/org/apache/ignite/internal/IgniteExceptionArchTest.java b/modules/arch-test/src/test/java/org/apache/ignite/internal/IgniteExceptionArchTest.java
index 6cdcb2727c..eca5e80bb1 100644
--- a/modules/arch-test/src/test/java/org/apache/ignite/internal/IgniteExceptionArchTest.java
+++ b/modules/arch-test/src/test/java/org/apache/ignite/internal/IgniteExceptionArchTest.java
@@ -40,12 +40,12 @@ import org.apache.ignite.client.IgniteClientConnectionException;
import org.apache.ignite.client.IgniteClientFeatureNotSupportedByServerException;
import org.apache.ignite.internal.network.RecipientLeftException;
import org.apache.ignite.internal.network.UnresolvableConsistentIdException;
+import org.apache.ignite.lang.CursorClosedException;
import org.apache.ignite.lang.IgniteCheckedException;
import org.apache.ignite.lang.IgniteException;
import org.apache.ignite.lang.LocationProvider.RootLocationProvider;
import org.apache.ignite.security.exception.InvalidCredentialsException;
import org.apache.ignite.security.exception.UnsupportedAuthenticationTypeException;
-import org.apache.ignite.sql.CursorClosedException;
import org.apache.ignite.sql.NoRowSetExpectedException;
/**
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 1df14b84cf..51cf656195 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
@@ -18,7 +18,6 @@
package org.apache.ignite.internal.client.sql;
import static org.apache.ignite.internal.util.CompletableFutures.nullCompletedFuture;
-import static org.apache.ignite.lang.ErrorGroups.Sql.CURSOR_NO_MORE_PAGES_ERR;
import java.util.ArrayList;
import java.util.Collections;
@@ -34,13 +33,12 @@ import org.apache.ignite.internal.client.table.ClientSchema;
import org.apache.ignite.internal.marshaller.ClientMarshallerReader;
import org.apache.ignite.internal.marshaller.Marshaller;
import org.apache.ignite.internal.marshaller.MarshallerException;
+import org.apache.ignite.lang.CursorClosedException;
import org.apache.ignite.lang.ErrorGroups.Client;
import org.apache.ignite.lang.IgniteException;
import org.apache.ignite.sql.ColumnMetadata;
-import org.apache.ignite.sql.CursorClosedException;
import org.apache.ignite.sql.NoRowSetExpectedException;
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.apache.ignite.table.mapper.Mapper;
@@ -158,15 +156,10 @@ class ClientAsyncResultSet<T> implements AsyncResultSet<T> {
public CompletableFuture<? extends AsyncResultSet<T>> fetchNextPage() {
requireResultSet();
- if (closed) {
+ if (closed || !hasMorePages()) {
return CompletableFuture.failedFuture(new CursorClosedException());
}
- if (!hasMorePages()) {
- return CompletableFuture.failedFuture(
- new SqlException(CURSOR_NO_MORE_PAGES_ERR, "There are no more pages."));
- }
-
return ch.serviceAsync(
ClientOp.SQL_CURSOR_NEXT_PAGE,
w -> w.out().packLong(resourceId),
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/table/AbstractClientView.java b/modules/client/src/main/java/org/apache/ignite/internal/client/table/AbstractClientView.java
index d665f5b096..9e0dfa5fca 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/client/table/AbstractClientView.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/table/AbstractClientView.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.client.table;
import static java.util.stream.Collectors.toSet;
import static org.apache.ignite.internal.client.ClientUtils.sync;
+import static org.apache.ignite.internal.table.criteria.CriteriaExceptionMapperUtil.mapToPublicCriteriaException;
import static org.apache.ignite.internal.util.ExceptionUtils.unwrapCause;
import static org.apache.ignite.lang.util.IgniteNameUtils.parseSimpleName;
@@ -143,11 +144,14 @@ abstract class AbstractClientView<T> implements CriteriaQuerySource<T> {
return new QueryCriteriaAsyncCursor<>(resultSet, queryMapper(meta, schema), session::closeAsync);
})
- .exceptionally(th -> {
- session.closeAsync();
-
- throw new CompletionException(unwrapCause(th));
+ .whenComplete((ignore, err) -> {
+ if (err != null) {
+ session.closeAsync();
+ }
});
+ })
+ .exceptionally(th -> {
+ throw new CompletionException(mapToPublicCriteriaException(unwrapCause(th)));
});
}
}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/lang/SqlExceptionMapperUtil.java b/modules/core/src/main/java/org/apache/ignite/internal/table/criteria/CriteriaExceptionMapperUtil.java
similarity index 64%
copy from modules/sql-engine/src/main/java/org/apache/ignite/internal/lang/SqlExceptionMapperUtil.java
copy to modules/core/src/main/java/org/apache/ignite/internal/table/criteria/CriteriaExceptionMapperUtil.java
index a36119ccca..b9e1ec049f 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/lang/SqlExceptionMapperUtil.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/table/criteria/CriteriaExceptionMapperUtil.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.lang;
+package org.apache.ignite.internal.table.criteria;
import static org.apache.ignite.internal.lang.IgniteExceptionMapperUtil.mapToPublicException;
import static org.apache.ignite.lang.ErrorGroups.Common.INTERNAL_ERR;
@@ -23,42 +23,49 @@ import static org.apache.ignite.lang.ErrorGroups.Common.INTERNAL_ERR;
import org.apache.ignite.lang.ErrorGroups.Common;
import org.apache.ignite.lang.TraceableException;
import org.apache.ignite.sql.SqlException;
+import org.apache.ignite.table.criteria.CriteriaException;
/**
- * This utility class provides an ability to map Ignite internal exceptions to public SqlException.
+ * This utility class provides an ability to map internal SQL exceptions to public CriteriaException.
*/
-public class SqlExceptionMapperUtil {
-
+public class CriteriaExceptionMapperUtil {
/**
- * This method provides a mapping from internal exception to SQL public ones.
+ * This method provides a mapping from internal SQL exception to public ones.
*
* <p>The rules of mapping are the following:</p>
* <ul>
* <li>any instance of {@link Error} is returned as is, except {@link AssertionError}
- * that will always be mapped to {@link SqlException} with the {@link Common#INTERNAL_ERR} error code.</li>
- * <li>any instance of {@link TraceableException} is wrapped into {@link SqlException}
+ * that will always be mapped to {@link CriteriaException} with the {@link Common#INTERNAL_ERR} error code.</li>
+ * <li>any instance of {@link SqlException} is wrapped into {@link CriteriaException} with the {@link Common#INTERNAL_ERR}
+ * error code.</li>
+ * <li>any instance of {@link TraceableException} is wrapped into {@link CriteriaException}
* with the original {@link TraceableException#traceId() traceUd} and {@link TraceableException#code() code}.</li>
* <li>if there are no any mappers that can do a mapping from the given error to a public exception,
- * then {@link SqlException} with the {@link Common#INTERNAL_ERR} error code is returned.</li>
+ * then {@link CriteriaException} with the {@link Common#INTERNAL_ERR} error code is returned.</li>
* </ul>
*
* @param origin Exception to be mapped.
* @return Public exception.
*/
- public static Throwable mapToPublicSqlException(Throwable origin) {
+ public static Throwable mapToPublicCriteriaException(Throwable origin) {
Throwable e = mapToPublicException(origin);
+
if (e instanceof Error) {
return e;
}
- if (e instanceof SqlException) {
+ if (e instanceof CriteriaException) {
return e;
}
+ if (e instanceof SqlException) {
+ return new CriteriaException(INTERNAL_ERR, e);
+ }
+
if (e instanceof TraceableException) {
TraceableException traceable = (TraceableException) e;
- return new SqlException(traceable.traceId(), traceable.code(), e.getMessage(), e);
+ return new CriteriaException(traceable.traceId(), traceable.code(), e.getMessage(), e);
}
- return new SqlException(INTERNAL_ERR, origin);
+ return new CriteriaException(INTERNAL_ERR, e);
}
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/table/criteria/CursorAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/table/criteria/CursorAdapter.java
index d1684943ba..c2acf02e85 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/table/criteria/CursorAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/table/criteria/CursorAdapter.java
@@ -17,14 +17,14 @@
package org.apache.ignite.internal.table.criteria;
-import static org.apache.ignite.internal.lang.IgniteExceptionMapperUtil.convertToPublicFuture;
-import static org.apache.ignite.internal.lang.IgniteExceptionMapperUtil.mapToPublicException;
+import static org.apache.ignite.internal.util.ExceptionUtils.copyExceptionWithCause;
import static org.apache.ignite.internal.util.ExceptionUtils.sneakyThrow;
import static org.apache.ignite.internal.util.ExceptionUtils.unwrapCause;
import java.util.Iterator;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutionException;
import org.apache.ignite.lang.AsyncCursor;
import org.apache.ignite.lang.Cursor;
@@ -54,9 +54,13 @@ public class CursorAdapter<T> implements Cursor<T> {
@Override
public void close() {
try {
- convertToPublicFuture(ac.closeAsync().toCompletableFuture()).join();
- } catch (Throwable e) {
- throw sneakyThrow(mapToPublicException(unwrapCause(e)));
+ ac.closeAsync().toCompletableFuture().get();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt(); // Restore interrupt flag.
+
+ throw sneakyThrow(unwrapCause(e));
+ } catch (ExecutionException e) {
+ throw sneakyThrow(copyExceptionWithCause(e));
}
}
@@ -92,8 +96,8 @@ public class CursorAdapter<T> implements Cursor<T> {
} else if (nextPageStage != null) {
try {
curRes = nextPageStage.toCompletableFuture().join();
- } catch (CompletionException ex) {
- throw sneakyThrow(unwrapCause(ex));
+ } catch (CompletionException e) {
+ throw sneakyThrow(copyExceptionWithCause(e));
}
advance();
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/table/criteria/QueryCriteriaAsyncCursor.java b/modules/core/src/main/java/org/apache/ignite/internal/table/criteria/QueryCriteriaAsyncCursor.java
index 1880faaa1a..8c03b5e731 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/table/criteria/QueryCriteriaAsyncCursor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/table/criteria/QueryCriteriaAsyncCursor.java
@@ -17,11 +17,14 @@
package org.apache.ignite.internal.table.criteria;
+import static org.apache.ignite.internal.table.criteria.CriteriaExceptionMapperUtil.mapToPublicCriteriaException;
import static org.apache.ignite.internal.util.CollectionUtils.mapIterable;
+import static org.apache.ignite.internal.util.ExceptionUtils.sneakyThrow;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import org.apache.ignite.lang.AsyncCursor;
+import org.apache.ignite.sql.NoRowSetExpectedException;
import org.apache.ignite.sql.SqlRow;
import org.apache.ignite.sql.async.AsyncResultSet;
import org.jetbrains.annotations.Nullable;
@@ -57,13 +60,21 @@ public class QueryCriteriaAsyncCursor<T, R> implements AsyncCursor<T> {
/** {@inheritDoc} */
@Override
public Iterable<T> currentPage() {
- return mapIterable(ars.currentPage(), mapper, null);
+ try {
+ return mapIterable(ars.currentPage(), mapper, null);
+ } catch (NoRowSetExpectedException e) {
+ throw sneakyThrow(mapToPublicCriteriaException(e));
+ }
}
/** {@inheritDoc} */
@Override
public int currentPageSize() {
- return ars.currentPageSize();
+ try {
+ return ars.currentPageSize();
+ } catch (NoRowSetExpectedException e) {
+ throw sneakyThrow(mapToPublicCriteriaException(e));
+ }
}
/** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/AsyncCursor.java b/modules/core/src/main/java/org/apache/ignite/internal/util/AsyncCursor.java
index b5277c0f06..1d0c6c5505 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/AsyncCursor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/AsyncCursor.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.util;
import java.util.List;
import java.util.concurrent.CompletableFuture;
+import org.apache.ignite.lang.CursorClosedException;
/**
* Asynchronous cursor.
@@ -31,11 +32,11 @@ 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.sql.CursorClosedException} even if the future returned by {@link #closeAsync()}
- * is not completed yet.
+ * {@link CursorClosedException} even if the future returned by {@link #closeAsync()} is not completed yet.
*
* @param rows Desired amount of rows.
* @return A completion stage that will be completed with batch of size {@code rows} or less if there is no more data.
+ * @throws CursorClosedException If cursor is closed.
*/
CompletableFuture<BatchedResult<T>> requestNextAsync(int rows);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/AsyncWrapper.java b/modules/core/src/main/java/org/apache/ignite/internal/util/AsyncWrapper.java
index adcdd54b3d..f14fc9b43f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/AsyncWrapper.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/AsyncWrapper.java
@@ -26,7 +26,7 @@ import java.util.NoSuchElementException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Function;
-import org.apache.ignite.sql.CursorClosedException;
+import org.apache.ignite.lang.CursorClosedException;
/**
* Wrapper that converts a synchronous iterator to an asynchronous one.
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/table/criteria/CriteriaExceptionMapperUtilTest.java b/modules/core/src/test/java/org/apache/ignite/internal/table/criteria/CriteriaExceptionMapperUtilTest.java
new file mode 100644
index 0000000000..49a5dead8a
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/table/criteria/CriteriaExceptionMapperUtilTest.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.table.criteria;
+
+import static org.apache.ignite.internal.table.criteria.CriteriaExceptionMapperUtil.mapToPublicCriteriaException;
+import static org.apache.ignite.lang.ErrorGroups.Common.INTERNAL_ERR;
+import static org.apache.ignite.lang.ErrorGroups.Sql.EXECUTION_CANCELLED_ERR;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+import org.apache.ignite.internal.lang.IgniteInternalException;
+import org.apache.ignite.sql.NoRowSetExpectedException;
+import org.apache.ignite.table.criteria.CriteriaException;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests mapping internal exceptions to public {@link CriteriaException}.
+ */
+class CriteriaExceptionMapperUtilTest {
+ /**
+ * Tests a default mapping of internal exceptions passed from the sql engine.
+ */
+ @Test
+ public void testMappingForInternalException() {
+ IgniteInternalException internalErr = new IgniteInternalException(EXECUTION_CANCELLED_ERR);
+ Throwable mappedErr = mapToPublicCriteriaException(internalErr);
+
+ assertThat(mappedErr, instanceOf(CriteriaException.class));
+
+ CriteriaException mappedCriteriaErr = (CriteriaException) mappedErr;
+
+ assertThat("Mapped exception should have the same trace identifier.", mappedCriteriaErr.traceId(), is(internalErr.traceId()));
+ assertThat("Mapped exception shouldn't have the same error code.", mappedCriteriaErr.code(), is(INTERNAL_ERR));
+ }
+
+ /**
+ * Tests a default mapping of exceptions passed from the sql engine.
+ */
+ @Test
+ public void testMappingForSqlException() {
+ NoRowSetExpectedException sqlErr = new NoRowSetExpectedException();
+ Throwable mappedErr = mapToPublicCriteriaException(sqlErr);
+
+ assertThat(mappedErr, instanceOf(CriteriaException.class));
+
+ CriteriaException mappedCriteriaErr = (CriteriaException) mappedErr;
+
+ assertThat("Mapped exception should have the same trace identifier.", mappedCriteriaErr.traceId(), is(sqlErr.traceId()));
+ assertThat("Mapped exception shouldn't have the same error code.", mappedCriteriaErr.code(), is(INTERNAL_ERR));
+ }
+
+ /**
+ * Tests a default mapping of exceptions.
+ */
+ @Test
+ public void testMappingForCriteriaException() {
+ CriteriaException criteriaErr = new CriteriaException(INTERNAL_ERR, new NoRowSetExpectedException());
+
+ Throwable mappedErr = mapToPublicCriteriaException(criteriaErr);
+
+ assertSame(criteriaErr, mappedErr);
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/util/AsyncWrapperSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/util/AsyncWrapperSelfTest.java
index 6b88e84d93..3751211f7b 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/util/AsyncWrapperSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/util/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.testframework.BaseIgniteAbstractTest;
-import org.apache.ignite.sql.SqlException;
+import org.apache.ignite.lang.CursorClosedException;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
@@ -159,14 +159,14 @@ public class AsyncWrapperSelfTest extends BaseIgniteAbstractTest {
.thenAccept(batch -> assertThat(batch.items(), equalTo(data.subList(0, 1))))
.exceptionally(ex -> {
assertInstanceOf(CompletionException.class, ex);
- assertInstanceOf(SqlException.class, ex.getCause());
+ assertInstanceOf(CursorClosedException.class, ex.getCause());
return null;
});
var stage2 = cursor.closeAsync();
var stage3 = cursor.requestNextAsync(1)
.exceptionally(ex -> {
- assertInstanceOf(SqlException.class, ex);
+ assertInstanceOf(CursorClosedException.class, ex);
return null;
});
diff --git a/modules/platforms/cpp/ignite/common/error_codes.h b/modules/platforms/cpp/ignite/common/error_codes.h
index 03bdff6fe0..20ba88a4f2 100644
--- a/modules/platforms/cpp/ignite/common/error_codes.h
+++ b/modules/platforms/cpp/ignite/common/error_codes.h
@@ -63,6 +63,7 @@ enum class code : underlying_t {
ILLEGAL_ARGUMENT = 0x10003,
SSL_CONFIGURATION = 0x10004,
NODE_LEFT = 0x10005,
+ CURSOR_CLOSED = 0x10006,
INTERNAL = 0x1ffff,
// Table group. Group code: 2
@@ -87,10 +88,8 @@ enum class code : underlying_t {
HANDSHAKE_HEADER = 0x3000a,
// Sql group. Group code: 4
- CURSOR_NO_MORE_PAGES = 0x40001,
QUERY_NO_RESULT_SET = 0x40002,
SCHEMA_NOT_FOUND = 0x40003,
- CURSOR_CLOSED = 0x40004,
STMT_PARSE = 0x40005,
STMT_VALIDATION = 0x40006,
CONSTRAINT_VIOLATION = 0x40007,
diff --git a/modules/platforms/cpp/ignite/odbc/common_types.cpp b/modules/platforms/cpp/ignite/odbc/common_types.cpp
index 7883b0648f..9a3bde68c8 100644
--- a/modules/platforms/cpp/ignite/odbc/common_types.cpp
+++ b/modules/platforms/cpp/ignite/odbc/common_types.cpp
@@ -112,6 +112,8 @@ environment_attribute environment_attribute_to_internal(int32_t attr) {
sql_state error_code_to_sql_state(error::code code) {
switch (code) {
// Common group. Group code: 1
+ case error::code::CURSOR_CLOSED:
+ return sql_state::S24000_INVALID_CURSOR_STATE;
case error::code::NODE_STOPPING:
case error::code::COMPONENT_NOT_STARTED:
case error::code::ILLEGAL_ARGUMENT:
@@ -150,10 +152,6 @@ sql_state error_code_to_sql_state(error::code code) {
return sql_state::S08004_CONNECTION_REJECTED;
// Sql group. Group code: 4
- case error::code::CURSOR_CLOSED:
- return sql_state::S24000_INVALID_CURSOR_STATE;
- case error::code::CURSOR_NO_MORE_PAGES:
- return sql_state::S24000_INVALID_CURSOR_STATE;
case error::code::SCHEMA_NOT_FOUND:
return sql_state::S3F000_INVALID_SCHEMA_NAME;
case error::code::PLANNING_TIMEOUT:
diff --git a/modules/platforms/dotnet/Apache.Ignite/ErrorCodes.g.cs b/modules/platforms/dotnet/Apache.Ignite/ErrorCodes.g.cs
index 75cdff906c..36e5ad094f 100644
--- a/modules/platforms/dotnet/Apache.Ignite/ErrorCodes.g.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/ErrorCodes.g.cs
@@ -75,6 +75,9 @@ namespace Apache.Ignite
/// <summary> NodeLeft error. </summary>
public const int NodeLeft = (GroupCode << 16) | (5 & 0xFFFF);
+ /// <summary> CursorClosed error. </summary>
+ public const int CursorClosed = (GroupCode << 16) | (6 & 0xFFFF);
+
/// <summary> Internal error. </summary>
public const int Internal = (GroupCode << 16) | (65535 & 0xFFFF);
}
@@ -159,18 +162,12 @@ namespace Apache.Ignite
/// <summary> Sql group name. </summary>
public const String GroupName = "SQL";
- /// <summary> CursorNoMorePages error. </summary>
- public const int CursorNoMorePages = (GroupCode << 16) | (1 & 0xFFFF);
-
/// <summary> QueryNoResultSet error. </summary>
public const int QueryNoResultSet = (GroupCode << 16) | (2 & 0xFFFF);
/// <summary> SchemaNotFound error. </summary>
public const int SchemaNotFound = (GroupCode << 16) | (3 & 0xFFFF);
- /// <summary> CursorClosed error. </summary>
- public const int CursorClosed = (GroupCode << 16) | (4 & 0xFFFF);
-
/// <summary> StmtParse error. </summary>
public const int StmtParse = (GroupCode << 16) | (5 & 0xFFFF);
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Sql/ResultSet.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Sql/ResultSet.cs
index a2bd3a6f52..170c1a26c5 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Sql/ResultSet.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Sql/ResultSet.cs
@@ -403,7 +403,7 @@ namespace Apache.Ignite.Internal.Sql
if (_iterated)
{
- throw new IgniteClientException(ErrorGroups.Sql.CursorClosed, "Query result set can not be iterated more than once.");
+ throw new IgniteClientException(ErrorGroups.Common.CursorClosed, "Query result set can not be iterated more than once.");
}
_iterated = true;
diff --git a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItCommonApiTest.java b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItCommonApiTest.java
index a705c6ea4e..08ded62911 100644
--- a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItCommonApiTest.java
+++ b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItCommonApiTest.java
@@ -32,8 +32,8 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import org.apache.ignite.Ignite;
import org.apache.ignite.internal.sql.BaseSqlIntegrationTest;
+import org.apache.ignite.lang.CursorClosedException;
import org.apache.ignite.lang.IgniteException;
-import org.apache.ignite.sql.CursorClosedException;
import org.apache.ignite.sql.IgniteSql;
import org.apache.ignite.sql.ResultSet;
import org.apache.ignite.sql.Session;
diff --git a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlApiBaseTest.java b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlApiBaseTest.java
index d656ebe3dc..0616fadd41 100644
--- a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlApiBaseTest.java
+++ b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlApiBaseTest.java
@@ -43,13 +43,14 @@ import org.apache.ignite.internal.sql.BaseSqlIntegrationTest;
import org.apache.ignite.internal.sql.api.ColumnMetadataImpl.ColumnOriginImpl;
import org.apache.ignite.internal.testframework.IgniteTestUtils;
import org.apache.ignite.internal.tx.TxManager;
+import org.apache.ignite.lang.CursorClosedException;
+import org.apache.ignite.lang.ErrorGroups.Common;
import org.apache.ignite.lang.ErrorGroups.Sql;
import org.apache.ignite.lang.ErrorGroups.Transactions;
import org.apache.ignite.lang.IgniteException;
import org.apache.ignite.sql.BatchedArguments;
import org.apache.ignite.sql.ColumnMetadata;
import org.apache.ignite.sql.ColumnType;
-import org.apache.ignite.sql.CursorClosedException;
import org.apache.ignite.sql.IgniteSql;
import org.apache.ignite.sql.NoRowSetExpectedException;
import org.apache.ignite.sql.ResultSet;
@@ -480,12 +481,12 @@ public abstract class ItSqlApiBaseTest extends BaseSqlIntegrationTest {
ses.close();
- SqlException sqlEx = assertThrowsSqlException(
- Sql.CURSOR_CLOSED_ERR,
- "Cursor is closed",
- () -> rs.forEachRemaining(System.out::println));
+ IgniteTestUtils.assertThrowsWithCode(
+ CursorClosedException.class,
+ Common.CURSOR_CLOSED_ERR,
+ () -> rs.forEachRemaining(System.out::println),
+ "Cursor is closed");
- assertTrue(IgniteTestUtils.hasCause(sqlEx, CursorClosedException.class, null));
assertThrowsSqlException(Sql.SESSION_CLOSED_ERR, "Session is closed", () -> execute(ses, "SELECT ID FROM TEST"));
}
@@ -538,11 +539,11 @@ public abstract class ItSqlApiBaseTest extends BaseSqlIntegrationTest {
Thread.sleep(300); // ResultSetImpl fetches next page in background, wait to it to complete to avoid flakiness.
rs.close();
- assertThrowsSqlException(
+ IgniteTestUtils.assertThrowsWithCode(
CursorClosedException.class,
- Sql.CURSOR_CLOSED_ERR,
- "Cursor is closed",
- () -> rs.forEachRemaining(Object::hashCode));
+ Common.CURSOR_CLOSED_ERR,
+ () -> rs.forEachRemaining(Object::hashCode),
+ "Cursor is closed");
}
}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/lang/SqlExceptionMapperUtil.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/lang/SqlExceptionMapperUtil.java
index a36119ccca..33be23771a 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/lang/SqlExceptionMapperUtil.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/lang/SqlExceptionMapperUtil.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.lang;
import static org.apache.ignite.internal.lang.IgniteExceptionMapperUtil.mapToPublicException;
import static org.apache.ignite.lang.ErrorGroups.Common.INTERNAL_ERR;
+import org.apache.ignite.lang.CursorClosedException;
import org.apache.ignite.lang.ErrorGroups.Common;
import org.apache.ignite.lang.TraceableException;
import org.apache.ignite.sql.SqlException;
@@ -36,6 +37,7 @@ public class SqlExceptionMapperUtil {
* <ul>
* <li>any instance of {@link Error} is returned as is, except {@link AssertionError}
* that will always be mapped to {@link SqlException} with the {@link Common#INTERNAL_ERR} error code.</li>
+ * <li>any instance of {@link SqlException}, {@link CursorClosedException} is returned as is</li>
* <li>any instance of {@link TraceableException} is wrapped into {@link SqlException}
* with the original {@link TraceableException#traceId() traceUd} and {@link TraceableException#code() code}.</li>
* <li>if there are no any mappers that can do a mapping from the given error to a public exception,
@@ -50,7 +52,7 @@ public class SqlExceptionMapperUtil {
if (e instanceof Error) {
return e;
}
- if (e instanceof SqlException) {
+ if (e instanceof SqlException || e instanceof CursorClosedException) {
return e;
}
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 4bd0ea638f..13e2422cb9 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
@@ -17,8 +17,6 @@
package org.apache.ignite.internal.sql.api;
-import static org.apache.ignite.lang.ErrorGroups.Sql.CURSOR_NO_MORE_PAGES_ERR;
-
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -34,7 +32,6 @@ import org.apache.ignite.internal.util.AsyncCursor.BatchedResult;
import org.apache.ignite.internal.util.TransformingIterator;
import org.apache.ignite.sql.NoRowSetExpectedException;
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.apache.ignite.table.Tuple;
@@ -44,9 +41,6 @@ import org.jetbrains.annotations.Nullable;
* Asynchronous result set implementation.
*/
public class AsyncResultSetImpl<T> implements AsyncResultSet<T> {
- private static final CompletableFuture<? extends AsyncResultSet<?>> HAS_NO_MORE_PAGE_FUTURE =
- CompletableFuture.failedFuture(new SqlException(CURSOR_NO_MORE_PAGES_ERR, "There are no more pages."));
-
private final IdleExpirationTracker expirationTracker;
private final AsyncSqlCursor<InternalSqlRow> cursor;
@@ -141,20 +135,16 @@ public class AsyncResultSetImpl<T> implements AsyncResultSet<T> {
expirationTracker.touch();
- if (!hasMorePages()) {
- return (CompletableFuture<? extends AsyncResultSet<T>>) HAS_NO_MORE_PAGE_FUTURE;
- } else {
- return cursor.requestNextAsync(pageSize)
- .thenApply(page -> {
- curPage = page;
+ return cursor.requestNextAsync(pageSize)
+ .thenApply(page -> {
+ curPage = page;
- if (!curPage.hasMore()) {
- closeAsync();
- }
+ if (!curPage.hasMore()) {
+ closeAsync();
+ }
- return this;
- });
- }
+ return this;
+ });
}
/** {@inheritDoc} */
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 0f8b2f5408..e0026a757d 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
@@ -30,7 +30,7 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import org.apache.ignite.internal.sql.engine.QueryCancelledException;
import org.apache.ignite.internal.util.AsyncCursor;
-import org.apache.ignite.sql.CursorClosedException;
+import org.apache.ignite.lang.CursorClosedException;
import org.jetbrains.annotations.Nullable;
/**
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/lang/SqlExceptionMapperUtilTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/lang/SqlExceptionMapperUtilTest.java
index 807909ecdc..4b05df7263 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/lang/SqlExceptionMapperUtilTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/lang/SqlExceptionMapperUtilTest.java
@@ -25,9 +25,14 @@ import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertSame;
+import java.util.stream.Stream;
+import org.apache.ignite.lang.CursorClosedException;
import org.apache.ignite.sql.NoRowSetExpectedException;
import org.apache.ignite.sql.SqlException;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
/**
* Tests mapping internal exceptions to public SqlException.
@@ -49,16 +54,22 @@ class SqlExceptionMapperUtilTest {
assertThat("Mapped exception shouldn't have the same error code.", mappedSqlErr.code(), is(INTERNAL_ERR));
}
+ private static Stream<Arguments> testSqlInternalExceptionDefaultMappingForPublicException() {
+ return Stream.of(
+ Arguments.of(new NoRowSetExpectedException()),
+ Arguments.of(new CursorClosedException())
+ );
+ }
+
/**
* Tests a default mapping of internal exceptions passed from the sql engine.
*/
- @Test
- public void testSqlInternalExceptionDefaultMappingForSqlException() {
- NoRowSetExpectedException sqlErr = new NoRowSetExpectedException();
-
- Throwable mappedErr = mapToPublicSqlException(sqlErr);
+ @ParameterizedTest
+ @MethodSource
+ public void testSqlInternalExceptionDefaultMappingForPublicException(Throwable err) {
+ Throwable mappedErr = mapToPublicSqlException(err);
- assertSame(sqlErr, mappedErr);
+ assertSame(err, mappedErr);
}
/**
diff --git a/modules/table/src/integrationTest/java/org/apache/ignite/internal/table/ItCriteriaQueryTest.java b/modules/table/src/integrationTest/java/org/apache/ignite/internal/table/ItCriteriaQueryTest.java
index 754682b0aa..b136f6f706 100644
--- a/modules/table/src/integrationTest/java/org/apache/ignite/internal/table/ItCriteriaQueryTest.java
+++ b/modules/table/src/integrationTest/java/org/apache/ignite/internal/table/ItCriteriaQueryTest.java
@@ -23,6 +23,7 @@ import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
import static org.apache.ignite.internal.testframework.IgniteTestUtils.assertThrows;
+import static org.apache.ignite.internal.testframework.IgniteTestUtils.assertThrowsWithCode;
import static org.apache.ignite.internal.testframework.IgniteTestUtils.await;
import static org.apache.ignite.internal.testframework.matchers.TupleMatcher.tupleValue;
import static org.apache.ignite.lang.util.IgniteNameUtils.quote;
@@ -63,10 +64,12 @@ import org.apache.ignite.internal.sql.api.IgniteSqlImpl;
import org.apache.ignite.internal.util.IgniteUtils;
import org.apache.ignite.lang.AsyncCursor;
import org.apache.ignite.lang.Cursor;
-import org.apache.ignite.lang.IgniteException;
+import org.apache.ignite.lang.CursorClosedException;
+import org.apache.ignite.lang.ErrorGroups.Common;
import org.apache.ignite.table.RecordView;
import org.apache.ignite.table.Table;
import org.apache.ignite.table.Tuple;
+import org.apache.ignite.table.criteria.CriteriaException;
import org.apache.ignite.table.criteria.CriteriaQuerySource;
import org.apache.ignite.table.mapper.Mapper;
import org.apache.ignite.tx.Transaction;
@@ -152,7 +155,7 @@ public class ItCriteriaQueryTest extends ClusterPerClassIntegrationTest {
@MethodSource
public <T> void testRecordViewQuery(CriteriaQuerySource<T> view, Function<T, Tuple> mapper) {
assertThrows(
- IgniteException.class,
+ CriteriaException.class,
() -> view.query(null, columnValue("id", equalTo("2"))),
"Dynamic parameter requires adding explicit type cast"
);
@@ -257,7 +260,7 @@ public class ItCriteriaQueryTest extends ClusterPerClassIntegrationTest {
@MethodSource
public <T> void testKeyValueView(CriteriaQuerySource<T> view, Function<T, Entry<Tuple, Tuple>> mapper) {
assertThrows(
- IgniteException.class,
+ CriteriaException.class,
() -> view.query(null, columnValue("id", equalTo("2"))),
"Dynamic parameter requires adding explicit type cast"
);
@@ -413,6 +416,40 @@ public class ItCriteriaQueryTest extends ClusterPerClassIntegrationTest {
await(ars.closeAsync());
}
+ private static Stream<Arguments> allViews() {
+ Table table = CLUSTER.aliveNode().tables().table(TABLE_NAME);
+ Table clientTable = CLIENT.tables().table(TABLE_NAME);
+
+ return Stream.of(
+ Arguments.of(table.keyValueView()),
+ Arguments.of(table.keyValueView(TestObjectKey.class, TestObject.class)),
+ Arguments.of(clientTable.keyValueView()),
+ Arguments.of(clientTable.keyValueView(TestObjectKey.class, TestObject.class))
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("allViews")
+ void testFetchCursorIsClosed(CriteriaQuerySource<TestObject> view) {
+ AsyncCursor<TestObject> ars1 = await(view.queryAsync(null, null, builder().pageSize(2).build()));
+
+ assertNotNull(ars1);
+ await(ars1.closeAsync());
+ assertThrowsWithCode(CursorClosedException.class, Common.CURSOR_CLOSED_ERR, () -> await(ars1.fetchNextPage()), "Cursor is closed");
+
+ AsyncCursor<TestObject> ars2 = await(view.queryAsync(null, null, builder().pageSize(3).build()));
+
+ assertNotNull(ars2);
+ assertThrowsWithCode(CursorClosedException.class, Common.CURSOR_CLOSED_ERR, () -> await(ars2.fetchNextPage()), "Cursor is closed");
+ }
+
+ @ParameterizedTest
+ @MethodSource("allViews")
+ <T> void testInvalidColumnName(CriteriaQuerySource<T> view) {
+ assertThrows(CriteriaException.class, () -> await(view.queryAsync(null, columnValue("id1", equalTo(2)))),
+ "Unexpected column name: ID1");
+ }
+
private static Stream<Arguments> testRecordViewWithQuotes() {
Table table = CLUSTER.aliveNode().tables().table(QUOTED_TABLE_NAME);
Table clientTable = CLIENT.tables().table(QUOTED_TABLE_NAME);
@@ -494,7 +531,7 @@ public class ItCriteriaQueryTest extends ClusterPerClassIntegrationTest {
int baseSessionsCount = activeSessionsCount();
assertThrows(
- IgniteException.class,
+ CriteriaException.class,
() -> view.query(tx, columnValue("id", equalTo(2))),
"Transaction is already finished"
);
diff --git a/modules/table/src/main/java/org/apache/ignite/internal/table/AbstractTableView.java b/modules/table/src/main/java/org/apache/ignite/internal/table/AbstractTableView.java
index 66cb9dbe89..20980b1df2 100644
--- a/modules/table/src/main/java/org/apache/ignite/internal/table/AbstractTableView.java
+++ b/modules/table/src/main/java/org/apache/ignite/internal/table/AbstractTableView.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.table;
import static java.util.concurrent.CompletableFuture.completedFuture;
import static java.util.function.Function.identity;
import static org.apache.ignite.internal.lang.IgniteExceptionMapperUtil.convertToPublicFuture;
+import static org.apache.ignite.internal.table.criteria.CriteriaExceptionMapperUtil.mapToPublicCriteriaException;
import static org.apache.ignite.internal.util.ExceptionUtils.isOrCausedBy;
import static org.apache.ignite.internal.util.ExceptionUtils.sneakyThrow;
import static org.apache.ignite.internal.util.ExceptionUtils.unwrapCause;
@@ -213,12 +214,15 @@ abstract class AbstractTableView<R> implements CriteriaQuerySource<R> {
return new QueryCriteriaAsyncCursor<>(resultSet, queryMapper(meta, schema), session::closeAsync);
})
- .exceptionally(th -> {
- session.closeAsync();
-
- throw new CompletionException(unwrapCause(th));
+ .whenComplete((ignore, err) -> {
+ if (err != null) {
+ session.closeAsync();
+ }
});
- });
+ })
+ .exceptionally(th -> {
+ throw new CompletionException(mapToPublicCriteriaException(unwrapCause(th)));
+ });
}