This is an automated email from the ASF dual-hosted git repository.
amashenkov 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 126565d90fa IGNITE-26139 Sql. Jdbc. Support non-autocommit mode and
cancellation token for use in thin client API. (#6772)
126565d90fa is described below
commit 126565d90faee0a91ab69896d0befc2d1862bb4f
Author: Max Zhuravkov <[email protected]>
AuthorDate: Mon Oct 20 11:46:53 2025 +0300
IGNITE-26139 Sql. Jdbc. Support non-autocommit mode and cancellation token
for use in thin client API. (#6772)
---
.../ignite/jdbc/ItJdbcConnectionSelfTest.java | 2 +-
.../apache/ignite/jdbc/ItJdbcErrorsSelfTest.java | 1 -
.../ignite/jdbc/ItJdbcStatementCancelSelfTest.java | 25 +++----
.../apache/ignite/jdbc/ItJdbcTransactionTest.java | 3 +-
.../ignite/internal/jdbc2/JdbcConnection2.java | 87 +++++++++++++++++++++-
.../ignite/internal/jdbc2/JdbcStatement2.java | 23 ++++--
.../internal/jdbc2/JdbcConnection2SelfTest.java | 8 +-
.../org/apache/ignite/data/SpringDataJdbcTest.java | 2 +-
8 files changed, 117 insertions(+), 34 deletions(-)
diff --git
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionSelfTest.java
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionSelfTest.java
index 4959c0f8501..79076ce270a 100644
---
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionSelfTest.java
+++
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionSelfTest.java
@@ -530,7 +530,7 @@ public class ItJdbcConnectionSelfTest extends
AbstractJdbcSelfTest {
try (Connection conn = DriverManager.getConnection(URL)) {
// Should not be called in auto-commit mode
JdbcTestUtils.assertThrowsSqlException(
- "Transaction cannot be committed explicitly in auto-commit
mode.",
+ "Transaction cannot be rolled back explicitly in
auto-commit mode.",
conn::rollback
);
diff --git
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcErrorsSelfTest.java
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcErrorsSelfTest.java
index c852a1e5039..53d83aaa0f9 100644
---
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcErrorsSelfTest.java
+++
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcErrorsSelfTest.java
@@ -137,7 +137,6 @@ public class ItJdbcErrorsSelfTest extends
ItJdbcErrorsAbstractSelfTest {
* @throws SQLException if failed.
*/
@Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-26139")
public void testDdlWithDisabledAutoCommit() throws SQLException {
conn.setAutoCommit(false);
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 9048394ba49..80150ff660f 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
@@ -38,7 +38,6 @@ import org.junit.jupiter.api.Test;
/**
* Statement cancel test.
*/
-@Disabled("https://issues.apache.org/jira/browse/IGNITE-26139")
@SuppressWarnings({"ThrowableNotThrown",
"JDBCResourceOpenedButNotSafelyClosed"})
public class ItJdbcStatementCancelSelfTest extends AbstractJdbcSelfTest {
@AfterEach
@@ -78,6 +77,7 @@ 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);
@@ -115,10 +115,8 @@ public class ItJdbcStatementCancelSelfTest extends
AbstractJdbcSelfTest {
@Test
void fetchingNextPageAfterCancelingShouldThrow() throws Exception {
- stmt.setFetchSize(50);
-
{
- ResultSet rs = stmt.executeQuery("SELECT * FROM system_range(0,
75)");
+ ResultSet rs = stmt.executeQuery("SELECT * FROM system_range(0,
7500)");
assertTrue(rs.next());
@@ -136,22 +134,20 @@ public class ItJdbcStatementCancelSelfTest extends
AbstractJdbcSelfTest {
{
// but new execute should work
- ResultSet rs = stmt.executeQuery("SELECT * FROM system_range(0,
75)");
+ ResultSet rs = stmt.executeQuery("SELECT * FROM system_range(0,
7500)");
//noinspection StatementWithEmptyBody
- while (rs.next()) { }
+ while (rs.next()) {
+ }
}
}
@Test
public void cancellationOfOneStatementShouldNotAffectAnother() throws
Exception {
- stmt.setFetchSize(50);
- try (Statement anotherStmt = conn.createStatement()) {
- anotherStmt.setFetchSize(50);
- ResultSet rs1 = stmt.executeQuery("SELECT * FROM system_range(0,
75)");
-
- ResultSet rs2 = anotherStmt.executeQuery("SELECT * FROM
system_range(0, 75)");
+ try (Statement anotherStmt = conn.createStatement()) {
+ ResultSet rs1 = stmt.executeQuery("SELECT * FROM system_range(0,
7500)");
+ ResultSet rs2 = anotherStmt.executeQuery("SELECT * FROM
system_range(0, 7500)");
stmt.cancel();
@@ -164,7 +160,8 @@ public class ItJdbcStatementCancelSelfTest extends
AbstractJdbcSelfTest {
);
//noinspection StatementWithEmptyBody
- while (rs2.next()) { }
+ while (rs2.next()) {
+ }
}
}
@@ -192,6 +189,7 @@ public class ItJdbcStatementCancelSelfTest extends
AbstractJdbcSelfTest {
}
@Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-26143")
void cancellationOfBatch() throws Exception {
stmt.executeUpdate("CREATE TABLE dummy (id INT PRIMARY KEY, val INT)");
stmt.addBatch("INSERT INTO dummy SELECT x, x FROM system_range(1, 1)");
@@ -214,6 +212,7 @@ public class ItJdbcStatementCancelSelfTest extends
AbstractJdbcSelfTest {
}
@Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-26190")
void cancellationOfPreparedBatch() throws Exception {
stmt.executeUpdate("CREATE TABLE dummy (id INT PRIMARY KEY, val INT)");
try (PreparedStatement ps = conn.prepareStatement("INSERT INTO dummy
SELECT x, x FROM system_range(?, ?)")) {
diff --git
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcTransactionTest.java
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcTransactionTest.java
index 88a491f2450..0924c40d690 100644
---
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcTransactionTest.java
+++
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcTransactionTest.java
@@ -44,7 +44,6 @@ import org.junit.jupiter.params.provider.CsvSource;
/**
* Verifies that SQL DML statements can use an explicit transaction using the
jdbc API.
*/
-@Disabled("https://issues.apache.org/jira/browse/IGNITE-26139")
public class ItJdbcTransactionTest extends AbstractJdbcSelfTest {
/** Insert query. */
private static final String SQL_INSERT = "insert into TEST values (%d,
'%s')";
@@ -190,6 +189,7 @@ public class ItJdbcTransactionTest extends
AbstractJdbcSelfTest {
* @throws Exception If failed.
*/
@Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-26143")
public void testBatch() throws Exception {
checkRollbackAndCommit((conn, start, cnt) -> {
try (Statement stmt = conn.createStatement()) {
@@ -211,6 +211,7 @@ public class ItJdbcTransactionTest extends
AbstractJdbcSelfTest {
* @throws Exception If failed.
*/
@Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-26190")
public void testBatchPrepared() throws Exception {
checkRollbackAndCommit((conn, start, cnt) -> {
try (PreparedStatement pstmt =
conn.prepareStatement(SQL_INSERT_PREPARED)) {
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 63046055fc3..fddc15cf342 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,8 +51,11 @@ 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;
/**
@@ -83,12 +86,27 @@ public class JdbcConnection2 implements Connection {
private static final String
TRANSACTION_CANNOT_BE_COMMITED_IN_AUTOCOMMIT_MODE =
"Transaction cannot be committed explicitly in auto-commit mode.";
+ private static final String COMMIT_REQUEST_FAILED
+ = "The transaction commit request failed.";
+
+ private static final String
TRANSACTION_CANNOT_BE_ROLLED_BACK_IN_AUTOCOMMIT_MODE =
+ "Transaction cannot be rolled back explicitly in auto-commit
mode.";
+
+ private static final String ROLLBACK_REQUEST_FAILED
+ = "The transaction rollback request failed.";
+
private static final String CANNOT_SET_TRANSACTION_NONE =
"Cannot set transaction isolation level to TRANSACTION_NONE.";
private static final String INVALID_TRANSACTION_ISOLATION_LEVEL =
"Invalid transaction isolation level.";
+ private static final String NO_TRANSACTION_TO_COMMIT =
+ "No transaction to commit.";
+
+ private static final String NO_TRANSACTION_TO_ROLLBACK =
+ "No transaction to rollback.";
+
private static final String SHARDING_KEYS_ARE_NOT_SUPPORTED =
"Sharding keys are not supported.";
@@ -125,6 +143,8 @@ public class JdbcConnection2 implements Connection {
private int networkTimeoutMillis;
+ private @Nullable Transaction transaction;
+
/**
* Creates new connection.
*
@@ -247,14 +267,63 @@ public class JdbcConnection2 implements Connection {
return sql;
}
+ @Nullable Transaction startTransactionIfNoAutoCommit() {
+ if (transaction == null && !autoCommit) {
+ transaction = igniteClient.transactions().begin(new
TransactionOptions().readOnly(false));
+ return transaction;
+ } else {
+ return transaction;
+ }
+ }
+
+ private void commitTx() throws SQLException {
+ Transaction tx = transaction;
+ if (tx == null) {
+ throw new SQLException(NO_TRANSACTION_TO_COMMIT);
+ }
+
+ // Null out the transaction first.
+ transaction = null;
+
+ try {
+ tx.commit();
+ } catch (Exception e) {
+ throw new SQLException(COMMIT_REQUEST_FAILED,
IgniteExceptionMapperUtil.mapToPublicException(e));
+ }
+ }
+
+ private void rollbackTx() throws SQLException {
+ Transaction tx = transaction;
+ if (tx == null) {
+ throw new SQLException(NO_TRANSACTION_TO_ROLLBACK);
+ }
+
+ // Null out the transaction first.
+ transaction = null;
+
+ try {
+ tx.rollback();
+ } catch (Exception e) {
+ throw new SQLException(ROLLBACK_REQUEST_FAILED,
IgniteExceptionMapperUtil.mapToPublicException(e));
+ }
+ }
+
/** {@inheritDoc} */
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
ensureNotClosed();
- // TODO https://issues.apache.org/jira/browse/IGNITE-26139 Implement
autocommit = false
- if (autoCommit != this.autoCommit) {
- this.autoCommit = autoCommit;
+ // If setAutoCommit is called and the auto-commit mode is not changed,
the call is a no-op.
+ if (this.autoCommit == autoCommit) {
+ return;
+ }
+
+ boolean wasAutoCommit = this.autoCommit;
+ // Autocommit should be changed even if commit fails.
+ this.autoCommit = autoCommit;
+ // If this method is called during a transaction and the auto-commit
mode is changed, the transaction is committed.
+ if (!wasAutoCommit && transaction != null) {
+ commitTx();
}
}
@@ -274,6 +343,8 @@ public class JdbcConnection2 implements Connection {
if (autoCommit) {
throw new
SQLException(TRANSACTION_CANNOT_BE_COMMITED_IN_AUTOCOMMIT_MODE);
}
+
+ commitTx();
}
/** {@inheritDoc} */
@@ -282,8 +353,10 @@ public class JdbcConnection2 implements Connection {
ensureNotClosed();
if (autoCommit) {
- throw new
SQLException(TRANSACTION_CANNOT_BE_COMMITED_IN_AUTOCOMMIT_MODE);
+ throw new
SQLException(TRANSACTION_CANNOT_BE_ROLLED_BACK_IN_AUTOCOMMIT_MODE);
}
+
+ rollbackTx();
}
/** {@inheritDoc} */
@@ -311,6 +384,12 @@ public class JdbcConnection2 implements Connection {
List<Exception> suppressedExceptions = null;
+ boolean wasAutoCommit = this.autoCommit;
+ // Rollback on close
+ if (!wasAutoCommit && transaction != null) {
+ rollbackTx();
+ }
+
lock.lock();
try {
if (closed) {
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 2e5d660da89..be61cee9d73 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
@@ -38,11 +38,13 @@ 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;
/**
@@ -101,6 +103,8 @@ public class JdbcStatement2 implements Statement {
private boolean closeOnCompletion;
+ private volatile @Nullable CancelHandle cancelHandle;
+
JdbcStatement2(
Connection connection,
IgniteSql igniteSql,
@@ -149,10 +153,8 @@ public class JdbcStatement2 implements Statement {
throw new SQLException("SQL query is empty.");
}
- // TODO https://issues.apache.org/jira/browse/IGNITE-26139 Implement
autocommit = false
- if (!connection.getAutoCommit()) {
- throw new UnsupportedOperationException("Explicit transactions are
not supported yet.");
- }
+ 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) {
@@ -185,11 +187,15 @@ public class JdbcStatement2 implements Statement {
ClientSql clientSql = (ClientSql) igniteSql;
+ // Cancel handle is not reusable, we should create a new one for each
execution.
+ CancelHandle handle = CancelHandle.create();
+ cancelHandle = handle;
+
AsyncResultSet<SqlRow> clientRs;
try {
- clientRs = clientSql.executeAsyncInternal(null,
+ clientRs = clientSql.executeAsyncInternal(tx,
(Mapper<SqlRow>) null,
- null,
+ handle.token(),
queryModifiers,
igniteStmt,
args
@@ -337,7 +343,10 @@ public class JdbcStatement2 implements Statement {
public void cancel() throws SQLException {
ensureNotClosed();
- throw new UnsupportedOperationException();
+ CancelHandle handle = cancelHandle;
+ if (handle != null) {
+ handle.cancel();
+ }
}
/** {@inheritDoc} */
diff --git
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcConnection2SelfTest.java
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcConnection2SelfTest.java
index abb622040a0..9064aeda374 100644
---
a/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcConnection2SelfTest.java
+++
b/modules/jdbc/src/test/java/org/apache/ignite/internal/jdbc2/JdbcConnection2SelfTest.java
@@ -207,17 +207,13 @@ public class JdbcConnection2SelfTest extends
BaseIgniteAbstractTest {
expectNotSupported(conn::getTypeMap);
expectNotSupported(() -> conn.setTypeMap(Map.of()));
- /*
- TODO https://issues.apache.org/jira/browse/IGNITE-26139 autocommit
!= true
-
- Requires autocommit = false
+ conn.setAutoCommit(false);
expectNotSupported(conn::setSavepoint);
expectNotSupported(() -> conn.setSavepoint("S"));
-
+
Savepoint savepoint = Mockito.mock(Savepoint.class);
expectNotSupported(() -> conn.rollback(savepoint));
expectNotSupported(() -> conn.releaseSavepoint(savepoint));
- */
// createStatement - not supported flags
diff --git
a/modules/spring/spring-data-ignite/src/test/java/org/apache/ignite/data/SpringDataJdbcTest.java
b/modules/spring/spring-data-ignite/src/test/java/org/apache/ignite/data/SpringDataJdbcTest.java
index f4e122c1050..c3d621a9091 100644
---
a/modules/spring/spring-data-ignite/src/test/java/org/apache/ignite/data/SpringDataJdbcTest.java
+++
b/modules/spring/spring-data-ignite/src/test/java/org/apache/ignite/data/SpringDataJdbcTest.java
@@ -71,7 +71,7 @@ import org.springframework.data.util.Streamable;
*/
@SpringBootTest(classes = TestApplication.class)
@ExtendWith(WorkDirectoryExtension.class)
-@Disabled("https://issues.apache.org/jira/browse/IGNITE-26139")
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-26190")
public class SpringDataJdbcTest extends BaseIgniteAbstractTest {
@WorkDirectory