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

Reply via email to