This is an automated email from the ASF dual-hosted git repository.
ppa pushed a commit to branch jdbc_over_thin_sql
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/jdbc_over_thin_sql by this
push:
new 31be73b1720 IGNITE-26142 Jdbc. Support multi-statement execution in
(Prepared)Statement.execute() using thin client SQL API (#6806)
31be73b1720 is described below
commit 31be73b1720fa618794f45aaef128a93fd27716b
Author: Max Zhuravkov <[email protected]>
AuthorDate: Thu Oct 23 09:31:36 2025 +0300
IGNITE-26142 Jdbc. Support multi-statement execution in
(Prepared)Statement.execute() using thin client SQL API (#6806)
---
.../cli/commands/sql/ItSqlCommandTest.java | 22 ++--
.../cli/commands/sql/ItSqlMultistatementTest.java | 6 -
.../cli/commands/sql/ItSqlReplCommandTest.java | 10 +-
.../sql/ClientSqlCursorNextResultRequest.java | 24 +++-
.../internal/jdbc/ItJdbcMetadataSelfTest.java | 29 ++---
.../jdbc2/ItJdbcClusterPerIntegrationTest.java | 124 +++++++++++++++++++++
.../apache/ignite/jdbc/AbstractJdbcSelfTest.java | 9 +-
.../apache/ignite/jdbc/ItJdbcBatchSelfTest.java | 12 +-
.../ignite/jdbc/ItJdbcMultiStatementSelfTest.java | 40 +++++--
.../apache/ignite/jdbc/ItJdbcQueryMetricsTest.java | 4 -
.../ignite/jdbc/ItJdbcStatementCancelSelfTest.java | 1 -
.../ignite/jdbc/ItJdbcStatementSelfTest.java | 10 +-
.../ignite/internal/jdbc2/ClientSyncResultSet.java | 46 ++++++++
.../internal/jdbc2/ClientSyncResultSetImpl.java | 89 +++++++++++++++
.../ignite/internal/jdbc2/JdbcConnection2.java | 11 +-
.../internal/jdbc2/JdbcExceptionMapperUtil.java | 56 ++++++++++
.../internal/jdbc2/JdbcPreparedStatement2.java | 9 +-
.../ignite/internal/jdbc2/JdbcResultSet.java | 64 ++++++-----
.../ignite/internal/jdbc2/JdbcStatement2.java | 98 ++++++++--------
.../ignite/internal/jdbc2/ResultSetWrapper.java | 99 ++++++++++++++++
.../internal/jdbc2/JdbcResultSet2SelfTest.java | 66 ++++++-----
.../internal/jdbc2/JdbcStatement2SelfTest.java | 4 +-
.../client/ItThinClientMultistatementSqlTest.java | 19 +++-
23 files changed, 660 insertions(+), 192 deletions(-)
diff --git
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlCommandTest.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlCommandTest.java
index 7226e48e2a5..1c691db8c40 100644
---
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlCommandTest.java
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlCommandTest.java
@@ -20,7 +20,6 @@ package org.apache.ignite.internal.cli.commands.sql;
import static
org.apache.ignite.internal.cli.core.exception.handler.SqlExceptionHandler.CLIENT_CONNECTION_FAILED_MESSAGE;
import static org.junit.jupiter.api.Assertions.assertAll;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -155,7 +154,6 @@ class ItSqlCommandTest extends CliSqlCommandTestBase {
@Test
@DisplayName("Should execute multiline sql script from file")
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
void multilineScript() {
String filePath = getClass().getResource("/multiline.sql").getPath();
execute("sql", "--file", filePath, "--jdbc-url", JDBC_URL);
@@ -177,13 +175,13 @@ class ItSqlCommandTest extends CliSqlCommandTestBase {
}
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
void exceptionHandler() {
execute("sql", "SELECT 1/0;", "--jdbc-url", JDBC_URL);
assertAll(
this::assertOutputIsEmpty,
- () -> assertErrOutputContains("SQL query execution error"),
+ // TODO https://issues.apache.org/jira/browse/IGNITE-26790
+ // () -> assertErrOutputContains("SQL query execution error"),
() -> assertErrOutputContains("Division by zero"),
() -> assertErrOutputDoesNotContain("Unknown error")
);
@@ -192,7 +190,8 @@ class ItSqlCommandTest extends CliSqlCommandTestBase {
assertAll(
this::assertOutputIsEmpty,
- () -> assertErrOutputContains("SQL query execution error"),
+ // TODO https://issues.apache.org/jira/browse/IGNITE-26790
+ // () -> assertErrOutputContains("SQL query execution error"),
() -> assertErrOutputContains("Object 'NOTEXISTEDTABLE' not
found"),
() -> assertErrOutputDoesNotContain("Unknown error")
);
@@ -200,7 +199,6 @@ class ItSqlCommandTest extends CliSqlCommandTestBase {
@Test
@DisplayName("An error should be displayed indicating that the script
transaction was not completed by the script.")
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
void scriptTxNotFinishedByScript() {
String expectedError = "Transaction block doesn't have a COMMIT
statement at the end.";
@@ -209,7 +207,8 @@ class ItSqlCommandTest extends CliSqlCommandTestBase {
assertAll(
this::assertOutputIsEmpty,
- () -> assertErrOutputContains("SQL query execution error"),
+ // TODO https://issues.apache.org/jira/browse/IGNITE-26790
+ // () -> assertErrOutputContains("SQL query execution
error"),
() -> assertErrOutputContains(expectedError),
() -> assertErrOutputDoesNotContain("Unknown error")
);
@@ -220,7 +219,8 @@ class ItSqlCommandTest extends CliSqlCommandTestBase {
assertAll(
this::assertOutputIsEmpty,
- () -> assertErrOutputContains("SQL query execution error"),
+ // TODO https://issues.apache.org/jira/browse/IGNITE-26790
+ // () -> assertErrOutputContains("SQL query execution
error"),
() -> assertErrOutputContains(expectedError),
() -> assertErrOutputDoesNotContain("Unknown error")
);
@@ -231,7 +231,8 @@ class ItSqlCommandTest extends CliSqlCommandTestBase {
assertAll(
this::assertOutputIsEmpty,
- () -> assertErrOutputContains("SQL query execution error"),
+ // TODO https://issues.apache.org/jira/browse/IGNITE-26790
+ // () -> assertErrOutputContains("SQL query execution
error"),
() -> assertErrOutputContains(expectedError),
() -> assertErrOutputDoesNotContain("Unknown error")
);
@@ -242,7 +243,8 @@ class ItSqlCommandTest extends CliSqlCommandTestBase {
assertAll(
this::assertOutputIsEmpty,
- () -> assertErrOutputContains("SQL query execution error"),
+ // TODO https://issues.apache.org/jira/browse/IGNITE-26790
+ // () -> assertErrOutputContains("SQL query execution
error"),
() -> assertErrOutputContains(expectedError),
() -> assertErrOutputDoesNotContain("Unknown error")
);
diff --git
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlMultistatementTest.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlMultistatementTest.java
index 057c23bf3cd..a194cc26c2c 100644
---
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlMultistatementTest.java
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlMultistatementTest.java
@@ -20,7 +20,6 @@ package org.apache.ignite.internal.cli.commands.sql;
import static org.junit.jupiter.api.Assertions.assertAll;
import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
/**
@@ -37,7 +36,6 @@ public class ItSqlMultistatementTest extends
CliSqlCommandTestBase {
* Test for multiple insert queries. It ensures that the output contains
the number of updated rows for each query.
*/
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
void sequentialUpdates() {
String testQuery = "insert into mytable(id) values (1);insert into
mytable(id) values (2), (3)";
String expectedOutput = "Updated 1 rows.\n"
@@ -53,7 +51,6 @@ public class ItSqlMultistatementTest extends
CliSqlCommandTestBase {
}
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
void sequentialSelects() {
String testQuery = "select * from mytable order by id; insert into
mytable(id) values(3); select * from mytable order by id;";
String expectedOutput =
@@ -87,7 +84,6 @@ public class ItSqlMultistatementTest extends
CliSqlCommandTestBase {
}
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
void deleteAfterInserts() {
String testQuery = "insert into mytable(id) values (1);insert into
mytable(id) values (2), (3); delete from mytable;";
String expectedOutput = "Updated 1 rows.\n"
@@ -104,7 +100,6 @@ public class ItSqlMultistatementTest extends
CliSqlCommandTestBase {
}
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
void updateAfterInserts() {
String testQuery = "insert into mytable(id) values (1);insert into
mytable(id) values (2), (3); update mytable set Name = 'Name1';";
String expectedOutput = "Updated 1 rows.\n"
@@ -121,7 +116,6 @@ public class ItSqlMultistatementTest extends
CliSqlCommandTestBase {
}
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
void sequentialCreateTable() {
String testQuery = "create table mytable1(id int primary key); create
table mytable2(id int primary key)";
String expectedOutput = "Updated 0 rows.\n"
diff --git
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlReplCommandTest.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlReplCommandTest.java
index 4f33ca79a18..4791dbea68a 100644
---
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlReplCommandTest.java
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlReplCommandTest.java
@@ -23,7 +23,6 @@ import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Replaces;
import org.apache.ignite.internal.cli.CliIntegrationTest;
import org.apache.ignite.internal.cli.core.repl.executor.ReplExecutorProvider;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -64,11 +63,11 @@ class ItSqlReplCommandTest extends CliIntegrationTest {
}
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
void multilineCommand() {
execute("CREATE TABLE MULTILINE_TABLE(K INT PRIMARY KEY); \n INSERT
INTO MULTILINE_TABLE VALUES(1);", "--jdbc-url", JDBC_URL);
assertAll(
+ // TODO https://issues.apache.org/jira/browse/IGNITE-26790
// The output from CREATE TABLE is: Updated 0 rows.
() -> assertOutputContains("Updated 0 rows."),
this::assertErrOutputIsEmpty
@@ -100,13 +99,13 @@ class ItSqlReplCommandTest extends CliIntegrationTest {
}
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
void exceptionHandler() {
execute("SELECT 1/0;", "--jdbc-url", JDBC_URL);
assertAll(
this::assertOutputIsEmpty,
- () -> assertErrOutputContains("SQL query execution error"),
+ // TODO https://issues.apache.org/jira/browse/IGNITE-26790
+ // () -> assertErrOutputContains("SQL query execution error"),
() -> assertErrOutputContains("Division by zero"),
() -> assertErrOutputDoesNotContain("Unknown error")
);
@@ -115,7 +114,8 @@ class ItSqlReplCommandTest extends CliIntegrationTest {
assertAll(
this::assertOutputIsEmpty,
- () -> assertErrOutputContains("SQL query execution error"),
+ // TODO https://issues.apache.org/jira/browse/IGNITE-26790
+ // () -> assertErrOutputContains("SQL query execution error"),
() -> assertErrOutputContains("Object 'NOTEXISTEDTABLE' not
found"),
() -> assertErrOutputDoesNotContain("Unknown error")
);
diff --git
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlCursorNextResultRequest.java
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlCursorNextResultRequest.java
index 5e8752fae65..613ea270992 100644
---
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlCursorNextResultRequest.java
+++
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlCursorNextResultRequest.java
@@ -27,6 +27,7 @@ import
org.apache.ignite.client.handler.requests.sql.ClientSqlCommon.CursorWithP
import org.apache.ignite.internal.client.proto.ClientMessageUnpacker;
import org.apache.ignite.internal.lang.IgniteInternalCheckedException;
import org.apache.ignite.internal.sql.api.AsyncResultSetImpl;
+import org.apache.ignite.internal.sql.engine.AsyncSqlCursor;
import org.apache.ignite.sql.SqlRow;
/**
@@ -50,7 +51,7 @@ public class ClientSqlCursorNextResultRequest {
CursorWithPageSize cursorWithPageSize =
resource.get(CursorWithPageSize.class);
int pageSize = cursorWithPageSize.pageSize();
- return cursorWithPageSize.cursorFuture()
+ CompletableFuture<ResponseWriter> f = cursorWithPageSize.cursorFuture()
.thenComposeAsync(cur -> cur.requestNextAsync(pageSize)
.thenApply(batchRes -> new AsyncResultSetImpl<SqlRow>(
cur,
@@ -60,5 +61,26 @@ public class ClientSqlCursorNextResultRequest {
).thenCompose(asyncResultSet ->
ClientSqlCommon.writeResultSetAsync(resources,
asyncResultSet, metrics, pageSize, false, false, true)
).thenApply(rsWriter -> rsWriter), operationExecutor);
+
+ f.whenCompleteAsync((r, t) -> {
+ if (t != null) {
+ cursorWithPageSize.cursorFuture().thenAccept(cur ->
closeRemainingCursors(cur, false, operationExecutor));
+ }
+ }, operationExecutor);
+
+ return f;
+ }
+
+ private static void closeRemainingCursors(AsyncSqlCursor<?> cursor,
boolean closeCursor, Executor operationExecutor) {
+ if (cursor.hasNextResult()) {
+ cursor.nextResult().whenCompleteAsync((c, err) -> {
+ if (c != null) {
+ cursor.closeAsync();
+ closeRemainingCursors(c, true, operationExecutor);
+ }
+ }, operationExecutor);
+ } else if (closeCursor) {
+ cursor.closeAsync();
+ }
}
}
diff --git
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/internal/jdbc/ItJdbcMetadataSelfTest.java
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/internal/jdbc/ItJdbcMetadataSelfTest.java
index 14ccdce26fc..2c95ec8e1c7 100644
---
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/internal/jdbc/ItJdbcMetadataSelfTest.java
+++
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/internal/jdbc/ItJdbcMetadataSelfTest.java
@@ -52,21 +52,21 @@ public class ItJdbcMetadataSelfTest extends
AbstractJdbcSelfTest {
/** Creates tables. */
@BeforeAll
public static void createTables() throws SQLException {
- // TODO https://issues.apache.org/jira/browse/IGNITE-26142 convert
these statements back into a script
try (Statement stmt = conn.createStatement()) {
- stmt.execute("CREATE SCHEMA IF NOT EXISTS PUBLIC;");
- stmt.execute("CREATE SCHEMA IF NOT EXISTS META;");
- stmt.execute("CREATE SCHEMA IF NOT EXISTS USER2;");
- stmt.execute("CREATE SCHEMA IF NOT EXISTS \"user0\";");
- stmt.execute("CREATE SCHEMA IF NOT EXISTS USER1;");
- stmt.execute("CREATE TABLE person(name VARCHAR(32), age INT, orgid
INT PRIMARY KEY);");
- stmt.execute("CREATE TABLE organization(id INT PRIMARY KEY, name
VARCHAR, bigdata DECIMAL(20, 10));");
- stmt.execute("CREATE TABLE user1.table1(id INT PRIMARY KEY);");
- stmt.execute("CREATE TABLE user2.\"table2\"(id INT PRIMARY KEY);");
- stmt.execute("CREATE TABLE \"user0\".\"table0\"(\"id\" INT PRIMARY
KEY);");
- stmt.execute("CREATE TABLE \"user0\".table0(id INT PRIMARY KEY);");
- stmt.execute("INSERT INTO person (orgid, name, age) VALUES (1,
'111', 111);");
- stmt.execute("INSERT INTO organization (id, name, bigdata) VALUES
(1, 'AAA', 10);");
+ stmt.execute("CREATE SCHEMA IF NOT EXISTS PUBLIC;"
+ + "CREATE SCHEMA IF NOT EXISTS META;"
+ + "CREATE SCHEMA IF NOT EXISTS USER2;"
+ + "CREATE SCHEMA IF NOT EXISTS \"user0\";"
+ + "CREATE SCHEMA IF NOT EXISTS USER1;"
+ + "CREATE TABLE person(name VARCHAR(32), age INT, orgid
INT PRIMARY KEY);"
+ + "CREATE TABLE organization(id INT PRIMARY KEY, name
VARCHAR, bigdata DECIMAL(20, 10));"
+ + "CREATE TABLE user1.table1(id INT PRIMARY KEY);"
+ + "CREATE TABLE user2.\"table2\"(id INT PRIMARY KEY);"
+ + "CREATE TABLE \"user0\".\"table0\"(\"id\" INT PRIMARY
KEY);"
+ + "CREATE TABLE \"user0\".table0(id INT PRIMARY KEY);"
+ + "INSERT INTO person (orgid, name, age) VALUES (1, '111',
111);"
+ + "INSERT INTO organization (id, name, bigdata) VALUES (1,
'AAA', 10);"
+ );
}
}
@@ -161,6 +161,7 @@ public class ItJdbcMetadataSelfTest extends
AbstractJdbcSelfTest {
}
@Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-26145")
public void testResultSetMetaDataColumns() throws Exception {
createMetaTable();
diff --git
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/internal/jdbc2/ItJdbcClusterPerIntegrationTest.java
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/internal/jdbc2/ItJdbcClusterPerIntegrationTest.java
new file mode 100644
index 00000000000..06d37f12872
--- /dev/null
+++
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/internal/jdbc2/ItJdbcClusterPerIntegrationTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.jdbc2;
+
+import static
org.apache.ignite.internal.testframework.IgniteTestUtils.waitForCondition;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.Statement;
+import java.util.List;
+import org.apache.ignite.internal.ClusterPerTestIntegrationTest;
+import org.apache.ignite.jdbc.ItJdbcStatementSelfTest;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Jdbc tests that require a new cluster for each test.
+ */
+public class ItJdbcClusterPerIntegrationTest extends
ClusterPerTestIntegrationTest {
+
+ private static final List<String> STATEMENTS = List.of(
+ "SELECT 1; SELECT 2/0; SELECT 3",
+ "SELECT * FROM SYSTEM_RANGE(1, 1500); SELECT 2/0; SELECT 3",
+ "SELECT 1; SELECT 2/0; SELECT * FROM SYSTEM_RANGE(3, 1500)"
+ );
+
+ private static final String JDBC_URL =
"jdbc:ignite:thin://127.0.0.1:10800";
+
+ @Override
+ protected int initialNodes() {
+ return 1;
+ }
+
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-26789")
+ public void noScriptResourcesAfterExecutingFailingScript() throws
Exception {
+ for (String statement : STATEMENTS) {
+ log.info("Run statement: {}", statement);
+
+ try (Connection conn = DriverManager.getConnection(JDBC_URL)) {
+ Statement stmt = conn.createStatement();
+ stmt.execute(statement);
+ // Connection should close the statement
+ }
+ // Fails with IGN-CMN-65535 Failed to find resource with id: XYZ
+
+ expectNoResources();
+ }
+ }
+
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-26789")
+ public void noScriptResourcesAfterExecutingFailingScript2() throws
Exception {
+ for (String statement : STATEMENTS) {
+ log.info("Run statement: {}", statement);
+
+ try (Connection conn = DriverManager.getConnection(JDBC_URL)) {
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute(statement);
+ }
+ // Fails with IGN-CMN-65535 Failed to find resource with id:
XYZ
+
+ // All resources should be released
+ expectNoResources();
+ }
+ }
+ }
+
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-26789")
+ public void noResourcesScriptAfterClientTerminates() throws Exception {
+ for (String statement : STATEMENTS) {
+ log.info("Run statement: {}", statement);
+
+ Connection conn = DriverManager.getConnection(JDBC_URL);
+ JdbcConnection2 jdbcConnection =
conn.unwrap(JdbcConnection2.class);
+
+ Statement stmt = conn.createStatement();
+ // Do not close the statement, closing the client should release
its resources.
+ stmt.execute(statement);
+
+ // Close the client
+ jdbcConnection.closeClient();
+
+ expectNoResources();
+ }
+ }
+
+ @Test
+ public void noStatementResourcesAfterClientTerminates() throws Exception {
+ Connection conn = DriverManager.getConnection(JDBC_URL);
+ JdbcConnection2 jdbcConnection = conn.unwrap(JdbcConnection2.class);
+
+ Statement stmt = conn.createStatement();
+ // Do not close the statement, closing the client should release its
resources.
+ stmt.executeQuery("SELECT * FROM SYSTEM_RANGE(1, 15000)");
+
+ // Close the client
+ jdbcConnection.closeClient();
+
+ expectNoResources();
+ }
+
+ private void expectNoResources() throws InterruptedException {
+ assertTrue(waitForCondition(() ->
ItJdbcStatementSelfTest.openResources(cluster) == 0, 5_000));
+ assertTrue(waitForCondition(() ->
ItJdbcStatementSelfTest.openCursors(cluster) == 0, 5_000));
+ }
+}
diff --git
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/AbstractJdbcSelfTest.java
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/AbstractJdbcSelfTest.java
index 87237f7fb18..5470ea102f6 100644
---
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/AbstractJdbcSelfTest.java
+++
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/AbstractJdbcSelfTest.java
@@ -25,6 +25,7 @@ import java.sql.DriverManager;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Statement;
import java.util.Map;
+import org.apache.ignite.internal.Cluster;
import org.apache.ignite.internal.ClusterPerClassIntegrationTest;
import org.apache.ignite.internal.app.IgniteImpl;
import org.apache.ignite.internal.manager.IgniteComponent;
@@ -138,8 +139,8 @@ public class AbstractJdbcSelfTest extends
ClusterPerClassIntegrationTest {
}
/** Return a size of stored resources. Reflection based implementation,
need to be refactored. */
- int openResources() {
- IgniteImpl ignite = unwrapIgniteImpl(CLUSTER.node(0));
+ public static int openResources(Cluster cluster) {
+ IgniteImpl ignite = unwrapIgniteImpl(cluster.node(0));
IgniteComponent cliHnd = IgniteTestUtils.getFieldValue(ignite,
"clientHandlerModule");
Object clientInboundHandler = IgniteTestUtils.getFieldValue(cliHnd,
"handler");
Object rsrc = IgniteTestUtils.getFieldValue(clientInboundHandler,
"resources");
@@ -148,8 +149,8 @@ public class AbstractJdbcSelfTest extends
ClusterPerClassIntegrationTest {
}
/** Returns a size of opened cursors. */
- int openCursors() {
- IgniteImpl ignite = unwrapIgniteImpl(CLUSTER.node(0));
+ public static int openCursors(Cluster cluster) {
+ IgniteImpl ignite = unwrapIgniteImpl(cluster.node(0));
SqlQueryProcessor queryProcessor = (SqlQueryProcessor)
ignite.queryEngine();
return queryProcessor.openedCursors();
}
diff --git
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcBatchSelfTest.java
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcBatchSelfTest.java
index 43d1e520fa5..a0f517a4197 100644
---
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcBatchSelfTest.java
+++
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcBatchSelfTest.java
@@ -43,7 +43,6 @@ import java.time.LocalTime;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
-import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.apache.ignite.internal.TestWrappers;
@@ -117,7 +116,7 @@ public class ItJdbcBatchSelfTest extends
AbstractJdbcSelfTest {
statement.executeUpdate(SQL_DELETE);
}
- resourcesBefore = openResources();
+ resourcesBefore = openResources(CLUSTER);
}
/** {@inheritDoc} */
@@ -138,7 +137,7 @@ public class ItJdbcBatchSelfTest extends
AbstractJdbcSelfTest {
assertEquals(0, countOfPendingTransactions);
Awaitility.await().timeout(5, TimeUnit.SECONDS).untilAsserted(() -> {
- assertThat(openResources() - resourcesBefore, is(0));
+ assertThat(openResources(CLUSTER) - resourcesBefore, is(0));
});
}
@@ -811,8 +810,9 @@ public class ItJdbcBatchSelfTest extends
AbstractJdbcSelfTest {
}
// Each statement in a batch is executed separately, and timeout is
applied to each statement.
- {
- int timeoutMillis = ThreadLocalRandom.current().nextInt(1, 5);
+ // Retry until timeout exception is thrown.
+ Awaitility.await().untilAsserted(() -> {
+ int timeoutMillis = 1;
igniteStmt.timeout(timeoutMillis);
for (int i = 0; i < 3; i++) {
@@ -823,7 +823,7 @@ public class ItJdbcBatchSelfTest extends
AbstractJdbcSelfTest {
assertThrowsSqlException(SQLException.class,
"Query timeout", igniteStmt::executeBatch);
- }
+ });
{
// Disable timeout
diff --git
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcMultiStatementSelfTest.java
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcMultiStatementSelfTest.java
index 54326c2f5a5..86db5d79615 100644
---
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcMultiStatementSelfTest.java
+++
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcMultiStatementSelfTest.java
@@ -47,7 +47,6 @@ import org.junit.jupiter.params.provider.ValueSource;
/**
* Tests for queries containing multiple sql statements, separated by ";".
*/
-@Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
public class ItJdbcMultiStatementSelfTest extends AbstractJdbcSelfTest {
/**
* Setup tables.
@@ -70,17 +69,17 @@ public class ItJdbcMultiStatementSelfTest extends
AbstractJdbcSelfTest {
@AfterEach
void tearDown() throws Exception {
- int openCursorResources = openResources();
+ int openCursorResources = openResources(CLUSTER);
// connection + not closed result set
- assertTrue(openResources() <= 2, "Open cursors: " +
openCursorResources);
+ assertTrue(openResources(CLUSTER) <= 2, "Open cursors: " +
openCursorResources);
stmt.close();
- openCursorResources = openResources();
+ openCursorResources = openResources(CLUSTER);
// only connection context or 0 if already closed.
- assertTrue(openResources() <= 1, "Open cursors: " +
openCursorResources);
- assertTrue(waitForCondition(() -> openCursors() == 0, 5_000));
+ assertTrue(openResources(CLUSTER) <= 1, "Open cursors: " +
openCursorResources);
+ assertTrue(waitForCondition(() -> openCursors(CLUSTER) == 0, 5_000));
}
@Test
@@ -94,7 +93,9 @@ public class ItJdbcMultiStatementSelfTest extends
AbstractJdbcSelfTest {
// pk violation exception
// TODO: https://issues.apache.org/jira/browse/IGNITE-21133
stmt.execute("START TRANSACTION; INSERT INTO TEST_TX VALUES (1, 1,
'1'); COMMIT");
- assertThrowsSqlException("Failed to fetch query results", () ->
stmt.execute("SELECT COUNT(*) FROM TEST_TX"));
+ assertEquals(0, stmt.getUpdateCount());
+ assertThrowsSqlException("PK unique constraint is violated", () ->
stmt.getMoreResults());
+
stmt.execute("SELECT COUNT(*) FROM TEST_TX");
try (ResultSet rs = stmt.getResultSet()) {
assertTrue(rs.next());
@@ -145,18 +146,27 @@ public class ItJdbcMultiStatementSelfTest extends
AbstractJdbcSelfTest {
public void testSimpleQueryError() throws Exception {
boolean res = stmt.execute("SELECT 1; SELECT 1/0; SELECT 2");
assertTrue(res);
- assertThrowsSqlException("Failed to fetch query results", () ->
stmt.getMoreResults());
+ assertThrowsSqlException("Division by zero", () ->
stmt.getMoreResults());
// Next after exception.
- assertFalse(stmt.getMoreResults());
+ // assertFalse(stmt.getMoreResults());
+ // Do not move past the first result.
+ assertThrowsSqlException("Division by zero", () ->
stmt.getMoreResults());
stmt.closeOnCompletion();
}
+ @Test
+ public void testSimpleQueryErrorMustReleaseServerResources() throws
Exception {
+ // The script fails, the user does not retrieve any result sets.
+ stmt.execute("SELECT 1; SELECT 2/0; SELECT 3");
+ // But the resources must be released in after test callbacks.
+ }
+
@Test
public void testSimpleQueryErrorCloseRs() throws Exception {
- stmt.execute("SELECT 1; SELECT 1/0; SELECT 2");
+ stmt.execute("SELECT 1; SELECT 2/0; SELECT 2");
ResultSet rs = stmt.getResultSet();
- assertThrowsSqlException("Failed to fetch query results", () ->
stmt.getMoreResults());
+ assertThrowsSqlException("Division by zero", () ->
stmt.getMoreResults());
stmt.closeOnCompletion();
rs.close();
@@ -214,13 +224,18 @@ public class ItJdbcMultiStatementSelfTest extends
AbstractJdbcSelfTest {
@Test
public void noMoreResultsArePossibleAfterCloseOnCompletion() throws
Exception {
stmt.execute("SELECT 1; SELECT 2; SELECT 3");
+
+ ResultSet rs1 = stmt.getResultSet();
// SELECT 2;
assertTrue(stmt.getMoreResults());
+ assertTrue(rs1.isClosed());
+ ResultSet rs2 = stmt.getResultSet();
stmt.closeOnCompletion();
// SELECT 3;
assertTrue(stmt.getMoreResults());
+ assertTrue(rs2.isClosed());
assertFalse(stmt.isClosed());
// no more results, auto close statement
@@ -417,7 +432,7 @@ public class ItJdbcMultiStatementSelfTest extends
AbstractJdbcSelfTest {
@Test
@SuppressWarnings("ThrowableNotThrown")
public void testAutoCommitFalseTxControlStatementsNotSupported() throws
Exception {
- String txErrMsg = "Transaction control statements are not supported
when autocommit mode is disabled";
+ String txErrMsg = "Transaction control statements are not supported
when autocommit mode is disabled.";
conn.setAutoCommit(false);
assertThrowsSqlException(txErrMsg, () -> stmt.execute("START
TRANSACTION; SELECT 1; COMMIT"));
assertThrowsSqlException(txErrMsg, () -> stmt.execute("COMMIT"));
@@ -583,6 +598,7 @@ public class ItJdbcMultiStatementSelfTest extends
AbstractJdbcSelfTest {
}
@Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-26143")
public void testResultsFromExecuteBatch() throws Exception {
stmt.addBatch("INSERT INTO TEST_TX VALUES (7, 25, 'Michel');");
stmt.addBatch("INSERT INTO TEST_TX VALUES (8, 25, 'Michel');");
diff --git
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcQueryMetricsTest.java
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcQueryMetricsTest.java
index eca7adc20ae..f9a89af17ee 100644
---
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcQueryMetricsTest.java
+++
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcQueryMetricsTest.java
@@ -40,7 +40,6 @@ import org.apache.ignite.internal.metrics.MetricSet;
import org.apache.ignite.internal.sql.metrics.SqlQueryMetricSource;
import org.awaitility.Awaitility;
import org.hamcrest.Matchers;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
/**
@@ -58,7 +57,6 @@ public class ItJdbcQueryMetricsTest extends
AbstractJdbcSelfTest {
}
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
public void testScriptErrors() throws SQLException {
try (var stmt = conn.prepareStatement("SELECT 1; SELECT 1/?;")) {
stmt.setInt(1, 0);
@@ -93,7 +91,6 @@ public class ItJdbcQueryMetricsTest extends
AbstractJdbcSelfTest {
}
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
public void testScriptCancellation() throws SQLException {
try (var stmt = conn.prepareStatement("SELECT 1; SELECT 1/?;")) {
stmt.setInt(1, 0);
@@ -126,7 +123,6 @@ public class ItJdbcQueryMetricsTest extends
AbstractJdbcSelfTest {
}
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
public void testScriptTimeout() {
Callable<Map<String, Long>> runScript = () -> {
diff --git
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcStatementCancelSelfTest.java
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcStatementCancelSelfTest.java
index 936625b3a04..a49b0e07a29 100644
---
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcStatementCancelSelfTest.java
+++
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcStatementCancelSelfTest.java
@@ -77,7 +77,6 @@ public class ItJdbcStatementCancelSelfTest extends
AbstractJdbcSelfTest {
}
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
void cancellationOfMultiStatementQuery() throws Exception {
stmt.executeUpdate("CREATE TABLE dummy (id INT PRIMARY KEY, val INT)");
stmt.setFetchSize(1);
diff --git
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcStatementSelfTest.java
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcStatementSelfTest.java
index a40fa0aec41..100c87c7098 100644
---
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcStatementSelfTest.java
+++
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcStatementSelfTest.java
@@ -380,7 +380,6 @@ public class ItJdbcStatementSelfTest extends
ItJdbcAbstractStatementSelfTest {
}
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
public void testExecuteQueryMultipleOnlyResultSets() throws Exception {
assertTrue(conn.getMetaData().supportsMultipleResultSets());
@@ -414,7 +413,6 @@ public class ItJdbcStatementSelfTest extends
ItJdbcAbstractStatementSelfTest {
}
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
public void testExecuteQueryMultipleOnlyDml() throws Exception {
Statement stmt0 = conn.createStatement();
@@ -449,7 +447,6 @@ public class ItJdbcStatementSelfTest extends
ItJdbcAbstractStatementSelfTest {
}
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
public void testExecuteQueryMultipleMixed() throws Exception {
int stmtCnt = 10;
@@ -677,7 +674,6 @@ public class ItJdbcStatementSelfTest extends
ItJdbcAbstractStatementSelfTest {
}
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
public void testGetMoreResults() throws Exception {
assertFalse(stmt.getMoreResults());
@@ -787,7 +783,6 @@ public class ItJdbcStatementSelfTest extends
ItJdbcAbstractStatementSelfTest {
}
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
public void testStatementTypeMismatchSelectForCachedQuery() throws
Exception {
// Put query to cache.
stmt.executeQuery("select 1;");
@@ -820,7 +815,6 @@ public class ItJdbcStatementSelfTest extends
ItJdbcAbstractStatementSelfTest {
}
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26142")
public void testOpenCursorsPureQuery() throws Exception {
stmt.execute("SELECT 1; SELECT 2;");
ResultSet rs = stmt.getResultSet();
@@ -834,7 +828,7 @@ public class ItJdbcStatementSelfTest extends
ItJdbcAbstractStatementSelfTest {
}
stmt.close();
- assertTrue(waitForCondition(() -> openCursors() == 0, 5_000));
+ assertTrue(waitForCondition(() -> openCursors(CLUSTER) == 0, 5_000));
}
@Test
@@ -845,7 +839,7 @@ public class ItJdbcStatementSelfTest extends
ItJdbcAbstractStatementSelfTest {
stmt.execute("DROP TABLE T1");
stmt.getResultSet();
- assertTrue(waitForCondition(() -> openCursors() == 0, 5_000));
+ assertTrue(waitForCondition(() -> openCursors(CLUSTER) == 0, 5_000));
}
@Test
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/ClientSyncResultSet.java
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/ClientSyncResultSet.java
new file mode 100644
index 00000000000..a565e073947
--- /dev/null
+++
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/ClientSyncResultSet.java
@@ -0,0 +1,46 @@
+/*
+ * 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.jdbc2;
+
+import java.util.Iterator;
+import java.util.List;
+import org.apache.ignite.internal.sql.ResultSetMetadataImpl;
+import org.apache.ignite.sql.ResultSetMetadata;
+import org.apache.ignite.sql.SqlRow;
+
+/**
+ * Sync result set.
+ */
+interface ClientSyncResultSet extends Iterator<SqlRow> {
+
+ ResultSetMetadata EMPTY_METADATA = new ResultSetMetadataImpl(List.of());
+
+ ResultSetMetadata metadata();
+
+ boolean hasRowSet();
+
+ long affectedRows();
+
+ boolean wasApplied();
+
+ boolean hasNextResultSet();
+
+ ClientSyncResultSet nextResultSet();
+
+ void close();
+}
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/ClientSyncResultSetImpl.java
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/ClientSyncResultSetImpl.java
new file mode 100644
index 00000000000..2d1f49e909e
--- /dev/null
+++
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/ClientSyncResultSetImpl.java
@@ -0,0 +1,89 @@
+/*
+ * 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.jdbc2;
+
+import org.apache.ignite.internal.client.sql.ClientAsyncResultSet;
+import org.apache.ignite.internal.sql.SyncResultSetAdapter;
+import org.apache.ignite.sql.ResultSetMetadata;
+import org.apache.ignite.sql.SqlRow;
+
+/**
+ * Implementation of {@link ClientSyncResultSet}.
+ */
+final class ClientSyncResultSetImpl implements ClientSyncResultSet {
+
+ private final ClientAsyncResultSet<SqlRow> rs;
+
+ private final SyncResultSetAdapter<SqlRow> syncRs;
+
+ ClientSyncResultSetImpl(ClientAsyncResultSet<SqlRow> rs) {
+ this.rs = rs;
+ this.syncRs = new SyncResultSetAdapter<>(rs);
+ }
+
+ @Override
+ public ResultSetMetadata metadata() {
+ ResultSetMetadata metadata = syncRs.metadata();
+ return metadata != null ? metadata : EMPTY_METADATA;
+ }
+
+ @Override
+ public boolean hasRowSet() {
+ return syncRs.hasRowSet();
+ }
+
+ @Override
+ public long affectedRows() {
+ return syncRs.affectedRows();
+ }
+
+ @Override
+ public boolean wasApplied() {
+ return syncRs.wasApplied();
+ }
+
+ @Override
+ public boolean hasNextResultSet() {
+ return rs.hasNextResultSet();
+ }
+
+ @Override
+ public ClientSyncResultSet nextResultSet() {
+ if (!rs.hasNextResultSet()) {
+ throw new IllegalStateException("Should not have been called.");
+ }
+
+ ClientAsyncResultSet<SqlRow> nextRs = rs.nextResultSet().join();
+ return new ClientSyncResultSetImpl(nextRs);
+ }
+
+ @Override
+ public void close() {
+ syncRs.close();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return syncRs.hasNext();
+ }
+
+ @Override
+ public SqlRow next() {
+ return syncRs.next();
+ }
+}
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection2.java
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection2.java
index 1c0b3bc4741..e353df28164 100644
---
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection2.java
+++
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection2.java
@@ -51,12 +51,12 @@ import org.apache.ignite.internal.jdbc.ConnectionProperties;
import org.apache.ignite.internal.jdbc.JdbcDatabaseMetadata;
import org.apache.ignite.internal.jdbc.proto.JdbcQueryEventHandler;
import org.apache.ignite.internal.jdbc.proto.SqlStateCode;
-import org.apache.ignite.internal.lang.IgniteExceptionMapperUtil;
import org.apache.ignite.internal.sql.SqlCommon;
import org.apache.ignite.sql.IgniteSql;
import org.apache.ignite.tx.Transaction;
import org.apache.ignite.tx.TransactionOptions;
import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.TestOnly;
/**
* {@link Connection} implementation backed by the thin client.
@@ -282,7 +282,7 @@ public class JdbcConnection2 implements Connection {
try {
tx.commit();
} catch (Exception e) {
- throw new SQLException(COMMIT_REQUEST_FAILED,
IgniteExceptionMapperUtil.mapToPublicException(e));
+ throw
JdbcExceptionMapperUtil.mapToJdbcException(COMMIT_REQUEST_FAILED, e);
}
}
@@ -298,7 +298,7 @@ public class JdbcConnection2 implements Connection {
try {
tx.rollback();
} catch (Exception e) {
- throw new SQLException(ROLLBACK_REQUEST_FAILED,
IgniteExceptionMapperUtil.mapToPublicException(e));
+ throw
JdbcExceptionMapperUtil.mapToJdbcException(ROLLBACK_REQUEST_FAILED, e);
}
}
@@ -831,6 +831,11 @@ public class JdbcConnection2 implements Connection {
return properties;
}
+ @TestOnly
+ void closeClient() {
+ igniteClient.close();
+ }
+
private static void checkCursorOptions(
int resSetType,
int resSetConcurrency,
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcExceptionMapperUtil.java
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcExceptionMapperUtil.java
new file mode 100644
index 00000000000..34c3965ab38
--- /dev/null
+++
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcExceptionMapperUtil.java
@@ -0,0 +1,56 @@
+/*
+ * 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.jdbc2;
+
+import static org.apache.ignite.internal.util.ExceptionUtils.unwrapCause;
+
+import java.sql.SQLException;
+import org.apache.ignite.internal.lang.IgniteExceptionMapperUtil;
+import org.apache.ignite.lang.ErrorGroups.Sql;
+import org.apache.ignite.lang.IgniteException;
+
+/**
+ * Maps an exception to a {@link SQLException}.
+ */
+final class JdbcExceptionMapperUtil {
+
+ private static final String TX_CONTROL_STATEMENT_WHEN_AUTOCOMMIT_MODE_OFF
=
+ "Transaction control statements are not supported when autocommit
mode is disabled.";
+
+ private JdbcExceptionMapperUtil() {
+
+ }
+
+ static SQLException mapToJdbcException(String message, Exception e) {
+ return new SQLException(message,
IgniteExceptionMapperUtil.mapToPublicException(unwrapCause(e)));
+ }
+
+ static SQLException mapToJdbcException(Exception e) {
+ Throwable cause =
IgniteExceptionMapperUtil.mapToPublicException(unwrapCause(e));
+ String message = cause.getMessage();
+
+ if (cause instanceof IgniteException) {
+ IgniteException ie = (IgniteException) cause;
+ if (ie.code() == Sql.TX_CONTROL_INSIDE_EXTERNAL_TX_ERR) {
+ message = TX_CONTROL_STATEMENT_WHEN_AUTOCOMMIT_MODE_OFF;
+ }
+ }
+
+ return new SQLException(message, cause);
+ }
+}
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement2.java
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement2.java
index 3e6148a5957..114eb5f5d0f 100644
---
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement2.java
+++
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement2.java
@@ -58,7 +58,6 @@ import java.util.UUID;
import org.apache.ignite.internal.client.sql.ClientSql;
import org.apache.ignite.internal.jdbc.proto.IgniteQueryErrorCode;
import org.apache.ignite.internal.jdbc.proto.SqlStateCode;
-import org.apache.ignite.internal.lang.IgniteExceptionMapperUtil;
import org.apache.ignite.internal.util.CollectionUtils;
import org.apache.ignite.lang.CancelHandle;
import org.apache.ignite.sql.BatchedArguments;
@@ -179,8 +178,7 @@ public class JdbcPreparedStatement2 extends JdbcStatement2
implements PreparedSt
IgniteQueryErrorCode.UNKNOWN,
longsArrayToIntsArrayUnsafe(e.updateCounters()));
} catch (Exception e) {
- Throwable cause =
IgniteExceptionMapperUtil.mapToPublicException(e);
- throw new SQLException(cause.getMessage(), cause);
+ throw JdbcExceptionMapperUtil.mapToJdbcException(e);
} finally {
batchedArgs = null;
}
@@ -233,7 +231,10 @@ public class JdbcPreparedStatement2 extends JdbcStatement2
implements PreparedSt
public boolean execute() throws SQLException {
execute0(ALL, sql, currentArguments.toArray());
- return isQuery();
+ ResultSetWrapper rs = result;
+ assert rs != null;
+
+ return rs.isQuery();
}
/** {@inheritDoc} */
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
index ad6d4b1d39a..6863ffc40c3 100644
---
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
+++
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
@@ -52,13 +52,10 @@ import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
-import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Supplier;
import org.apache.ignite.internal.jdbc.proto.SqlStateCode;
-import org.apache.ignite.internal.lang.IgniteExceptionMapperUtil;
-import org.apache.ignite.internal.sql.ResultSetMetadataImpl;
import org.apache.ignite.internal.util.StringUtils;
import org.apache.ignite.sql.ColumnMetadata;
import org.apache.ignite.sql.ColumnType;
@@ -77,13 +74,11 @@ public class JdbcResultSet implements ResultSet {
private static final String SQL_SPECIFIC_TYPES_ARE_NOT_SUPPORTED =
"SQL-specific types are not supported.";
- private static final ResultSetMetadata EMPTY_METADATA = new
ResultSetMetadataImpl(List.of());
-
private static final BigDecimal MIN_DOUBLE =
BigDecimal.valueOf(-Double.MAX_VALUE);
private static final BigDecimal MAX_DOUBLE =
BigDecimal.valueOf(Double.MAX_VALUE);
- private final org.apache.ignite.sql.ResultSet<SqlRow> rs;
+ private final ClientSyncResultSet rs;
private final ResultSetMetadata rsMetadata;
@@ -103,7 +98,7 @@ public class JdbcResultSet implements ResultSet {
private int currentPosition;
- private boolean closed;
+ boolean closed;
private boolean wasNull;
@@ -111,7 +106,7 @@ public class JdbcResultSet implements ResultSet {
* Constructor.
*/
JdbcResultSet(
- org.apache.ignite.sql.ResultSet<SqlRow> rs,
+ ClientSyncResultSet rs,
Statement statement,
Supplier<ZoneId> zoneIdSupplier,
boolean closeOnCompletion,
@@ -119,9 +114,7 @@ public class JdbcResultSet implements ResultSet {
) {
this.rs = rs;
- ResultSetMetadata metadata = rs.metadata();
- this.rsMetadata = metadata != null ? metadata : EMPTY_METADATA;
-
+ this.rsMetadata = rs.metadata();
this.zoneIdSupplier = zoneIdSupplier;
this.statement = statement;
this.currentRow = null;
@@ -132,21 +125,36 @@ public class JdbcResultSet implements ResultSet {
this.maxRows = maxRows;
}
- int updateCount() {
- assert !isQuery() : "Should not be called on a query";
- if (rs.wasApplied() || rs.affectedRows() == -1) {
- return 0;
- } else {
- //noinspection NumericCastThatLosesPrecision
- return (int) rs.affectedRows();
+ ClientSyncResultSet resultSet() {
+ return rs;
+ }
+
+ @Nullable
+ JdbcResultSet tryNextResultSet() throws SQLException {
+ if (!rs.hasNextResultSet()) {
+ return null;
}
+
+ ClientSyncResultSet clientResultSet;
+
+ try {
+ clientResultSet = rs.nextResultSet();
+ } catch (Exception e) {
+ throw JdbcExceptionMapperUtil.mapToJdbcException(e);
+ }
+
+ JdbcStatement2 jdbcStatement = statement.unwrap(JdbcStatement2.class);
+ // isCloseOnCompletion throws SQLException if a statement is closed
+ boolean closeOnCompletion = jdbcStatement.closeOnCompletion;
+
+ return new JdbcResultSet(clientResultSet, statement, zoneIdSupplier,
closeOnCompletion, maxRows);
}
- boolean isQuery() {
- return rs.hasRowSet();
+ boolean isCloseOnCompletion() {
+ return closeOnCompletion;
}
- void closeStatement(boolean close) {
+ void setCloseStatement(boolean close) {
closeOnCompletion = close;
}
@@ -167,8 +175,7 @@ public class JdbcResultSet implements ResultSet {
currentPosition += 1;
return true;
} catch (Exception e) {
- Throwable cause =
IgniteExceptionMapperUtil.mapToPublicException(e);
- throw new SQLException(cause.getMessage(), cause);
+ throw JdbcExceptionMapperUtil.mapToJdbcException(e);
}
}
@@ -179,13 +186,15 @@ public class JdbcResultSet implements ResultSet {
}
closed = true;
+ boolean moreResultSets = rs.hasNextResultSet();
+
try {
rs.close();
} catch (Exception e) {
- Throwable cause =
IgniteExceptionMapperUtil.mapToPublicException(e);
- throw new SQLException(cause.getMessage(), cause);
+ throw JdbcExceptionMapperUtil.mapToJdbcException(e);
} finally {
- if (closeOnCompletion) {
+ // Close the statement if this result set is the last one.
+ if (closeOnCompletion && !moreResultSets) {
JdbcStatement2 statement2 =
statement.unwrap(JdbcStatement2.class);
statement2.closeIfAllResultsClosed();
}
@@ -2102,8 +2111,7 @@ public class JdbcResultSet implements ResultSet {
return val;
} catch (Exception e) {
- Throwable cause =
IgniteExceptionMapperUtil.mapToPublicException(e);
- throw new SQLException("Unable to value for column: " + colIdx,
cause);
+ throw JdbcExceptionMapperUtil.mapToJdbcException("Unable to value
for column: " + colIdx, e);
}
}
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement2.java
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement2.java
index ea7656ba575..fa1251189cc 100644
---
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement2.java
+++
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement2.java
@@ -32,17 +32,15 @@ import java.util.EnumSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
+import org.apache.ignite.internal.client.sql.ClientAsyncResultSet;
import org.apache.ignite.internal.client.sql.ClientSql;
import org.apache.ignite.internal.client.sql.QueryModifier;
import org.apache.ignite.internal.jdbc.proto.SqlStateCode;
-import org.apache.ignite.internal.lang.IgniteExceptionMapperUtil;
-import org.apache.ignite.internal.sql.SyncResultSetAdapter;
import org.apache.ignite.internal.util.ArrayUtils;
import org.apache.ignite.lang.CancelHandle;
import org.apache.ignite.sql.IgniteSql;
import org.apache.ignite.sql.SqlRow;
import org.apache.ignite.sql.Statement.StatementBuilder;
-import org.apache.ignite.sql.async.AsyncResultSet;
import org.apache.ignite.table.mapper.Mapper;
import org.apache.ignite.tx.Transaction;
import org.jetbrains.annotations.Nullable;
@@ -92,7 +90,7 @@ public class JdbcStatement2 implements Statement {
private final int rsHoldability;
- private volatile @Nullable JdbcResultSet resultSet;
+ protected volatile @Nullable ResultSetWrapper result;
private long queryTimeoutMillis;
@@ -102,7 +100,7 @@ public class JdbcStatement2 implements Statement {
private volatile boolean closed;
- private boolean closeOnCompletion;
+ boolean closeOnCompletion;
volatile @Nullable CancelHandle cancelHandle;
@@ -123,7 +121,10 @@ public class JdbcStatement2 implements Statement {
public ResultSet executeQuery(String sql) throws SQLException {
execute0(QUERY, Objects.requireNonNull(sql),
ArrayUtils.OBJECT_EMPTY_ARRAY);
- ResultSet rs = getResultSet();
+ ResultSetWrapper currentRs = result;
+ assert currentRs != null;
+
+ ResultSet rs = currentRs.current();
if (rs == null) {
throw new SQLException("The query isn't SELECT query: " + sql,
SqlStateCode.PARSING_EXCEPTION);
@@ -132,9 +133,9 @@ public class JdbcStatement2 implements Statement {
return rs;
}
- JdbcResultSet createResultSet(org.apache.ignite.sql.ResultSet<SqlRow>
resultSet) throws SQLException {
- JdbcConnection2 connection2 = connection.unwrap(JdbcConnection2.class);
- ZoneId zoneId = connection2.properties().getConnectionTimeZone();
+ JdbcResultSet createResultSet(ClientSyncResultSet resultSet) throws
SQLException {
+ JdbcConnection2 jdbcConnection =
connection.unwrap(JdbcConnection2.class);
+ ZoneId zoneId = jdbcConnection.properties().getConnectionTimeZone();
return new JdbcResultSet(resultSet, this, () -> zoneId,
closeOnCompletion, maxRows);
}
@@ -157,16 +158,6 @@ public class JdbcStatement2 implements Statement {
JdbcConnection2 connection2 = connection.unwrap(JdbcConnection2.class);
Transaction tx = connection2.startTransactionIfNoAutoCommit();
- // TODO https://issues.apache.org/jira/browse/IGNITE-26142
multistatement.
- if (sql.indexOf(';') == -1 || sql.indexOf(';') == sql.length() - 1) {
- queryModifiers.remove(QueryModifier.ALLOW_MULTISTATEMENT);
- }
-
- // TODO https://issues.apache.org/jira/browse/IGNITE-26142
multistatement.
- if (queryModifiers.contains(QueryModifier.ALLOW_MULTISTATEMENT)) {
- throw new UnsupportedOperationException("Multi-statements are not
supported yet.");
- }
-
org.apache.ignite.sql.Statement igniteStmt =
createIgniteStatement(sql);
ClientSql clientSql = (ClientSql) igniteSql;
@@ -174,9 +165,9 @@ public class JdbcStatement2 implements Statement {
CancelHandle handle = CancelHandle.create();
cancelHandle = handle;
- AsyncResultSet<SqlRow> clientRs;
+ ClientAsyncResultSet<SqlRow> clientRs;
try {
- clientRs = clientSql.executeAsyncInternal(tx,
+ clientRs = (ClientAsyncResultSet<SqlRow>)
clientSql.executeAsyncInternal(tx,
(Mapper<SqlRow>) null,
handle.token(),
queryModifiers,
@@ -184,12 +175,9 @@ public class JdbcStatement2 implements Statement {
args
).join();
- SyncResultSetAdapter<SqlRow> syncRs = new
SyncResultSetAdapter<>(clientRs);
-
- resultSet = createResultSet(syncRs);
+ result = new ResultSetWrapper(createResultSet(new
ClientSyncResultSetImpl(clientRs)));
} catch (Exception e) {
- Throwable cause =
IgniteExceptionMapperUtil.mapToPublicException(e);
- throw new SQLException(cause.getMessage(), cause);
+ throw JdbcExceptionMapperUtil.mapToJdbcException(e);
}
}
@@ -200,14 +188,15 @@ public class JdbcStatement2 implements Statement {
execute0(DML_OR_DDL, sql, ArrayUtils.OBJECT_EMPTY_ARRAY);
- int rowCount = getUpdateCount();
+ ResultSetWrapper rs = result;
+ assert rs != null;
- if (isQuery()) {
+ if (rs.isQuery()) {
closeResults();
throw new SQLException("The query is not DML statement: " + sql);
}
- return Math.max(rowCount, 0);
+ return rs.updateCount();
}
/** {@inheritDoc} */
@@ -362,7 +351,10 @@ public class JdbcStatement2 implements Statement {
execute0(QueryModifier.ALL, Objects.requireNonNull(sql),
ArrayUtils.OBJECT_EMPTY_ARRAY);
- return isQuery();
+ ResultSetWrapper rs = result;
+ assert rs != null;
+
+ return rs.isQuery();
}
/** {@inheritDoc} */
@@ -411,7 +403,12 @@ public class JdbcStatement2 implements Statement {
public @Nullable ResultSet getResultSet() throws SQLException {
ensureNotClosed();
- return isQuery() ? resultSet : null;
+ ResultSetWrapper rs = result;
+ if (rs != null && rs.isQuery()) {
+ return rs.current();
+ } else {
+ return null;
+ }
}
/** {@inheritDoc} */
@@ -419,8 +416,8 @@ public class JdbcStatement2 implements Statement {
public int getUpdateCount() throws SQLException {
ensureNotClosed();
- JdbcResultSet rs = resultSet;
- if (rs == null || rs.isQuery()) {
+ ResultSetWrapper rs = result;
+ if (rs == null) {
return -1;
} else {
return rs.updateCount();
@@ -440,15 +437,20 @@ public class JdbcStatement2 implements Statement {
switch (current) {
case CLOSE_CURRENT_RESULT:
-
- JdbcResultSet currentRs = resultSet;
- if (currentRs == null) {
+ ResultSetWrapper currentResult = result;
+ if (currentResult == null) {
return false;
}
- resultSet = null;
- currentRs.close();
- return false;
+ boolean moreResults = currentResult.nextResultSet();
+ if (!moreResults) {
+ // next() closes the remaining result set if necessary
+ result = null;
+
+ return false;
+ } else {
+ return currentResult.isQuery();
+ }
case CLOSE_ALL_RESULTS:
case KEEP_CURRENT_RESULT:
@@ -623,9 +625,9 @@ public class JdbcStatement2 implements Statement {
closeOnCompletion = true;
- JdbcResultSet rs = resultSet;
+ ResultSetWrapper rs = result;
if (rs != null) {
- rs.closeStatement(true);
+ rs.setCloseStatement(true);
}
}
@@ -659,16 +661,6 @@ public class JdbcStatement2 implements Statement {
this.queryTimeoutMillis = timeoutMillis;
}
- protected boolean isQuery() {
- // This method is called after statement is executed, so the reference
points to a correct result set.
- // The statement is not expected to be used from multiple threads, so
this reference points to a correct result set.
- // getResultSet() performs its own result set checks.
- JdbcResultSet rs = resultSet;
- assert rs != null;
-
- return rs.isQuery();
- }
-
org.apache.ignite.sql.Statement createIgniteStatement(String sql) throws
SQLException {
StatementBuilder builder = igniteSql.statementBuilder()
.query(sql)
@@ -695,11 +687,11 @@ public class JdbcStatement2 implements Statement {
}
private void closeResults() throws SQLException {
- JdbcResultSet rs = resultSet;
+ ResultSetWrapper rs = result;
+ result = null;
if (rs != null) {
rs.close();
}
- resultSet = null;
}
/**
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/ResultSetWrapper.java
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/ResultSetWrapper.java
new file mode 100644
index 00000000000..fc600917da9
--- /dev/null
+++
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc2/ResultSetWrapper.java
@@ -0,0 +1,99 @@
+/*
+ * 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.jdbc2;
+
+import java.sql.SQLException;
+
+final class ResultSetWrapper {
+
+ private JdbcResultSet resultSet;
+
+ ResultSetWrapper(JdbcResultSet first) {
+ resultSet = first;
+ }
+
+ void setCloseStatement(boolean b) {
+ resultSet.setCloseStatement(b);
+ }
+
+ JdbcResultSet current() {
+ return resultSet;
+ }
+
+ boolean isQuery() {
+ ClientSyncResultSet rs = resultSet.resultSet();
+
+ return rs.hasRowSet();
+ }
+
+ int updateCount() {
+ JdbcResultSet rs = resultSet;
+
+ ClientSyncResultSet clientResultSet = rs.resultSet();
+ if (clientResultSet.hasRowSet()) {
+ return -1;
+ } else if (clientResultSet.affectedRows() == -1) {
+ // DDL or control statements
+ return 0;
+ } else {
+ return (int) clientResultSet.affectedRows();
+ }
+ }
+
+ boolean nextResultSet() throws SQLException {
+ JdbcResultSet current = resultSet;
+ if (current == null) {
+ return false;
+ }
+
+ try {
+ JdbcResultSet nextRs = current.tryNextResultSet();
+ boolean hasNext = nextRs != null;
+
+ resultSet = nextRs;
+
+ return hasNext;
+ } finally {
+ current.close();
+ }
+ }
+
+ void close() throws SQLException {
+ JdbcResultSet current = resultSet;
+ if (current == null || current.closed) {
+ return;
+ }
+ resultSet = null;
+
+ JdbcResultSet rs = current;
+
+ do {
+ // TODO: https://issues.apache.org/jira/browse/IGNITE-26789 Close
properly.
+ // w/o iteration over all cursors, the cursors that are not
retrieved hung in the void
+ // and are never released.
+ try {
+ rs = rs.tryNextResultSet();
+ } catch (SQLException ignore) {
+ // This is an execution related exception, ignore it.
+ break;
+ }
+ } while (rs != null);
+
+ current.close();
+ }
+}
diff --git
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSet2SelfTest.java
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSet2SelfTest.java
index 01bfc0cda3e..54d52830544 100644
---
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSet2SelfTest.java
+++
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSet2SelfTest.java
@@ -134,18 +134,18 @@ public class JdbcResultSet2SelfTest extends
JdbcResultSetBaseSelfTest {
}
@Test
- @SuppressWarnings("unchecked")
public void nextExceptionIsWrapped() {
// ClientResultSet hasNext() throws
{
Statement statement = Mockito.mock(Statement.class);
- org.apache.ignite.sql.ResultSet<SqlRow> igniteRs =
Mockito.mock(org.apache.ignite.sql.ResultSet.class);
+ ClientSyncResultSet clientRs =
Mockito.mock(ClientSyncResultSet.class);
+
when(clientRs.metadata()).thenReturn(ClientSyncResultSet.EMPTY_METADATA);
RuntimeException cause = new RuntimeException("Some error");
- when(igniteRs.hasNext()).thenThrow(cause);
+ when(clientRs.hasNext()).thenThrow(cause);
- ResultSet rs = new JdbcResultSet(igniteRs, statement,
ZoneId::systemDefault, false, 0);
+ ResultSet rs = new JdbcResultSet(clientRs, statement,
ZoneId::systemDefault, false, 0);
SQLException err = assertThrows(SQLException.class, rs::next);
assertEquals("Some error", err.getMessage());
@@ -157,13 +157,14 @@ public class JdbcResultSet2SelfTest extends
JdbcResultSetBaseSelfTest {
{
Statement statement = Mockito.mock(Statement.class);
- org.apache.ignite.sql.ResultSet<SqlRow> igniteRs =
Mockito.mock(org.apache.ignite.sql.ResultSet.class);
+ ClientSyncResultSet clientRs =
Mockito.mock(ClientSyncResultSet.class);
+
when(clientRs.metadata()).thenReturn(ClientSyncResultSet.EMPTY_METADATA);
RuntimeException cause = new RuntimeException("Some error");
- when(igniteRs.hasNext()).thenReturn(true);
- when(igniteRs.next()).thenThrow(cause);
+ when(clientRs.hasNext()).thenReturn(true);
+ when(clientRs.next()).thenThrow(cause);
- ResultSet rs = new JdbcResultSet(igniteRs, statement,
ZoneId::systemDefault, false, 0);
+ ResultSet rs = new JdbcResultSet(clientRs, statement,
ZoneId::systemDefault, false, 0);
SQLException err = assertThrows(SQLException.class, rs::next);
assertEquals("Some error", err.getMessage());
@@ -173,40 +174,40 @@ public class JdbcResultSet2SelfTest extends
JdbcResultSetBaseSelfTest {
}
@Test
- @SuppressWarnings("unchecked")
public void closeClosesResultSet() throws SQLException {
Statement statement = Mockito.mock(Statement.class);
- org.apache.ignite.sql.ResultSet<SqlRow> igniteRs =
Mockito.mock(org.apache.ignite.sql.ResultSet.class);
- when(igniteRs.metadata()).thenReturn(new
ResultSetMetadataImpl(List.of()));
+ ClientSyncResultSet clientRs = Mockito.mock(ClientSyncResultSet.class);
+
when(clientRs.metadata()).thenReturn(ClientSyncResultSet.EMPTY_METADATA);
JdbcStatement2 statement2 = Mockito.mock(JdbcStatement2.class);
when(statement.unwrap(JdbcStatement2.class)).thenReturn(statement2);
- ResultSet rs = new JdbcResultSet(igniteRs, statement,
ZoneId::systemDefault, true, 0);
+ ResultSet rs = new JdbcResultSet(clientRs, statement,
ZoneId::systemDefault, true, 0);
rs.close();
rs.close();
- verify(igniteRs, times(1)).close();
- verify(igniteRs, times(1)).metadata();
+ verify(clientRs, times(1)).close();
+ verify(clientRs, times(1)).metadata();
verify(statement2, times(1)).closeIfAllResultsClosed();
- verifyNoMoreInteractions(igniteRs, statement2);
+ verify(clientRs, times(1)).hasNextResultSet();
+ verifyNoMoreInteractions(clientRs, statement2);
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
- @SuppressWarnings("unchecked")
public void closeExceptionIsWrapped(boolean closeOnCompletion) throws
SQLException {
JdbcStatement2 statement = Mockito.mock(JdbcStatement2.class);
when(statement.unwrap(JdbcStatement2.class)).thenReturn(statement);
- org.apache.ignite.sql.ResultSet<SqlRow> igniteRs =
Mockito.mock(org.apache.ignite.sql.ResultSet.class);
+ ClientSyncResultSet clientRs = Mockito.mock(ClientSyncResultSet.class);
+
when(clientRs.metadata()).thenReturn(ClientSyncResultSet.EMPTY_METADATA);
RuntimeException cause = new RuntimeException("Some error");
- doAnswer(new ThrowsException(cause)).when(igniteRs).close();
+ doAnswer(new ThrowsException(cause)).when(clientRs).close();
- ResultSet rs = new JdbcResultSet(igniteRs, statement,
ZoneId::systemDefault, closeOnCompletion, 0);
+ ResultSet rs = new JdbcResultSet(clientRs, statement,
ZoneId::systemDefault, closeOnCompletion, 0);
SQLException err = assertThrows(SQLException.class, rs::close);
assertEquals("Some error", err.getMessage());
@@ -218,19 +219,19 @@ public class JdbcResultSet2SelfTest extends
JdbcResultSetBaseSelfTest {
public void getValueExceptionIsWrapped() throws SQLException {
Statement statement = Mockito.mock(Statement.class);
- org.apache.ignite.sql.ResultSet<SqlRow> igniteRs =
Mockito.mock(org.apache.ignite.sql.ResultSet.class);
+ ClientSyncResultSet clientRs = Mockito.mock(ClientSyncResultSet.class);
SqlRow row = Mockito.mock(SqlRow.class);
ColumnMetadataImpl column = new ColumnMetadataImpl("C",
ColumnType.INT32, 0, 0, false, null);
- when(igniteRs.metadata()).thenReturn(new
ResultSetMetadataImpl(List.of(column)));
- when(igniteRs.hasNext()).thenReturn(true);
- when(igniteRs.next()).thenReturn(row);
+ when(clientRs.metadata()).thenReturn(new
ResultSetMetadataImpl(List.of(column)));
+ when(clientRs.hasNext()).thenReturn(true);
+ when(clientRs.next()).thenReturn(row);
RuntimeException cause = new RuntimeException("Corrupted value");
when(row.value(0)).thenThrow(cause);
- JdbcResultSet rs = new JdbcResultSet(igniteRs, statement,
ZoneId::systemDefault, false, 0);
+ JdbcResultSet rs = new JdbcResultSet(clientRs, statement,
ZoneId::systemDefault, false, 0);
assertTrue(rs.next());
SQLException err = assertThrows(SQLException.class, () ->
rs.getValue(1));
@@ -291,7 +292,6 @@ public class JdbcResultSet2SelfTest extends
JdbcResultSetBaseSelfTest {
return createResultSet(statement, zoneId, cols, rows, 0);
}
- @SuppressWarnings("unchecked")
private static ResultSet createResultSet(
Statement statement,
@SuppressWarnings("unused")
@@ -311,8 +311,8 @@ public class JdbcResultSet2SelfTest extends
JdbcResultSetBaseSelfTest {
// ResultSet has no metadata
if (cols.isEmpty() && rows.isEmpty()) {
- org.apache.ignite.sql.ResultSet<SqlRow> rs =
Mockito.mock(org.apache.ignite.sql.ResultSet.class);
- when(rs.metadata()).thenReturn(null);
+ ClientSyncResultSet rs = Mockito.mock(ClientSyncResultSet.class);
+ when(rs.metadata()).thenReturn(ClientSyncResultSet.EMPTY_METADATA);
return new JdbcResultSet(rs, statement, zoneIdSupplier, false, 0);
}
@@ -332,7 +332,7 @@ public class JdbcResultSet2SelfTest extends
JdbcResultSetBaseSelfTest {
return new JdbcResultSet(new ResultSetStub(apiMeta, rows), statement,
zoneIdSupplier, false, maxRows);
}
- private static class ResultSetStub implements
org.apache.ignite.sql.ResultSet<SqlRow> {
+ private static class ResultSetStub implements ClientSyncResultSet {
private final ResultSetMetadata meta;
private final Iterator<List<Object>> it;
private @Nullable List<Object> current;
@@ -363,6 +363,16 @@ public class JdbcResultSet2SelfTest extends
JdbcResultSetBaseSelfTest {
throw new IllegalStateException("Should not be called");
}
+ @Override
+ public boolean hasNextResultSet() {
+ return false;
+ }
+
+ @Override
+ public ClientSyncResultSet nextResultSet() {
+ throw new IllegalStateException("Should not be called");
+ }
+
@Override
public void close() {
// Does nothing, checked separately.
diff --git
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStatement2SelfTest.java
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStatement2SelfTest.java
index 0f1f8b87070..9b820028415 100644
---
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStatement2SelfTest.java
+++
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStatement2SelfTest.java
@@ -37,7 +37,6 @@ import
org.apache.ignite.internal.jdbc.ConnectionPropertiesImpl;
import org.apache.ignite.internal.sql.ResultSetMetadataImpl;
import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
import org.apache.ignite.sql.IgniteSql;
-import org.apache.ignite.sql.SqlRow;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import org.mockito.Mockito;
@@ -379,8 +378,7 @@ public class JdbcStatement2SelfTest extends
BaseIgniteAbstractTest {
try (Statement stmt = createStatement()) {
JdbcStatement2 jdbc = stmt.unwrap(JdbcStatement2.class);
- @SuppressWarnings("unchecked")
- org.apache.ignite.sql.ResultSet<SqlRow> igniteRs =
Mockito.mock(org.apache.ignite.sql.ResultSet.class);
+ ClientSyncResultSet igniteRs =
Mockito.mock(ClientSyncResultSet.class);
when(igniteRs.metadata()).thenReturn(new
ResultSetMetadataImpl(List.of()));
{
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientMultistatementSqlTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientMultistatementSqlTest.java
index bfb3ab34645..32e93d14e77 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientMultistatementSqlTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientMultistatementSqlTest.java
@@ -418,6 +418,21 @@ public class ItThinClientMultistatementSqlTest extends
ItAbstractThinClientTest
});
}
+ @Test
+ public void executeScriptWithErrors() {
+ client().sql().executeScript("SELECT 1; SELECT 2/0; SELECT 3;");
+ }
+
+ @Test
+ public void iterateOverScriptWithErrors() {
+ ClientAsyncResultSet<SqlRow> rs = runSql("SELECT 1; SELECT 2/0; SELECT
3; SELECT 4;");
+ try {
+ rs.nextResultSet().join();
+ } catch (Exception ignore) {
+ // Resources should not leak after this error.
+ }
+ }
+
private void expectRowsCount(@Nullable Transaction tx, String table, long
expectedCount) {
try (ResultSet<SqlRow> rs = client().sql().execute(tx, "SELECT
COUNT(*) FROM " + table)) {
assertThat(rs.next().longValue(0), is(expectedCount));
@@ -444,7 +459,7 @@ public class ItThinClientMultistatementSqlTest extends
ItAbstractThinClientTest
return count;
}
- private ClientAsyncResultSet<SqlRow> runSql(String query, Object ... args)
{
+ private ClientAsyncResultSet<SqlRow> runSql(String query, Object... args) {
return runSql(null, null, null, query, args);
}
@@ -474,7 +489,7 @@ public class ItThinClientMultistatementSqlTest extends
ItAbstractThinClientTest
executeSql(null, sql, args);
}
- private void executeSql(@Nullable Transaction tx, String sql, Object ...
args) {
+ private void executeSql(@Nullable Transaction tx, String sql, Object...
args) {
fetchAllResults(runSql(tx, null, null, sql, args))
.forEach(rs -> await(rs.closeAsync()));
}