Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
6ce4606
Add comprehensive MST transaction metadata and edge case tests
vikrantpuppala Mar 26, 2026
2929c4d
Rename test table to avoid collisions with Python driver tests
vikrantpuppala Mar 26, 2026
a075958
Add ignoreTransactions=0 to JDBC URL and fix 2 test issues
vikrantpuppala Mar 26, 2026
18bfbc5
Fix 8 test failures based on E2E run results
vikrantpuppala Mar 27, 2026
dc758c7
Add CI workflow for transaction tests and use env vars for credentials
vikrantpuppala Mar 27, 2026
18a9fb2
Revert "Add CI workflow for transaction tests and use env vars for cr…
vikrantpuppala Mar 27, 2026
1669d40
Add MST transaction test design document
vikrantpuppala Apr 6, 2026
3a8dc22
Add mermaid diagrams to MST test design document
vikrantpuppala Apr 6, 2026
ad061fe
Address PR review comments on MST test design
vikrantpuppala Apr 6, 2026
bfa8df3
Move auto-start tests to JdbcApiTransactionTests
vikrantpuppala Apr 6, 2026
f5845bb
Implement MST test design: parameterized test hierarchy for SEA/Thrift
vikrantpuppala Apr 6, 2026
15f40c9
Fix MST test infrastructure issues
vikrantpuppala Apr 6, 2026
7f6748a
Fix SEA statement reuse: use fresh statements per SQL execution
vikrantpuppala Apr 6, 2026
f3e5270
Fix MST test assertions based on E2E results
vikrantpuppala Apr 6, 2026
9a894a2
Fix executeUpdate/executeLargeUpdate: they succeed on SEA (don't throw)
vikrantpuppala Apr 6, 2026
db23d9a
Fix executeUpdate/executeLargeUpdate SEA: silently poisons transaction
vikrantpuppala Apr 6, 2026
274a7c6
Skip PreparedStatement reuse and getMetaData tests on SEA
vikrantpuppala Apr 6, 2026
9103dca
Update MST test design doc with E2E findings
vikrantpuppala Apr 7, 2026
e0990ef
Fix D.5 getPrimaryKeys: assertThrows on both backends
vikrantpuppala Apr 7, 2026
fe9f9bd
Update design doc: D.5 getPrimaryKeys broken on both backends, fix co…
vikrantpuppala Apr 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
545 changes: 545 additions & 0 deletions docs/mst-test-design.md

Large diffs are not rendered by default.

This file was deleted.

1,292 changes: 0 additions & 1,292 deletions src/test/java/com/databricks/jdbc/integration/e2e/TransactionTests.java

This file was deleted.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
package com.databricks.jdbc.integration.e2e.mst;

import static org.junit.jupiter.api.Assertions.*;

import java.sql.*;
import java.util.stream.Stream;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

/**
* MST transaction tests using explicit SQL statements (BEGIN TRANSACTION / COMMIT / ROLLBACK).
*
* <p>Inherits 22 shared correctness tests from AbstractMstTestBase. Adds 7 SQL-specific tests for
* BEGIN TRANSACTION edge cases and SET AUTOCOMMIT command.
*
* <p>Parameterized by backend: SEA (UseThriftClient=0) and Thrift (UseThriftClient=1).
*/
public class ExplicitSqlTransactionTests extends AbstractMstTestBase {

static Stream<Arguments> backends() {
return Stream.of(Arguments.of(0, "SEA"), Arguments.of(1, "Thrift"));
}

private void init(int useThrift) throws SQLException {
initBackend(useThrift);
}

@AfterEach
void tearDown() {
cleanup();
}

// --- Transaction control via explicit SQL ---

@Override
protected void startTransaction(Connection conn) throws SQLException {
try (Statement stmt = conn.createStatement()) {
stmt.execute("BEGIN TRANSACTION");
}
}

@Override
protected void commitTransaction(Connection conn) throws SQLException {
try (Statement stmt = conn.createStatement()) {
stmt.execute("COMMIT");
}
}

@Override
protected void rollbackTransaction(Connection conn) throws SQLException {
try (Statement stmt = conn.createStatement()) {
stmt.execute("ROLLBACK");
}
}

// ========================== INHERITED TESTS (parameterized) ==========================

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testCommitSingleInsert(int useThrift, String backend) throws Exception {
init(useThrift);
super.testCommitSingleInsert();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testCommitMultipleInserts(int useThrift, String backend) throws Exception {
init(useThrift);
super.testCommitMultipleInserts();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testRollbackSingleInsert(int useThrift, String backend) throws Exception {
init(useThrift);
super.testRollbackSingleInsert();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testMultipleSequentialTransactions(int useThrift, String backend) throws Exception {
init(useThrift);
super.testMultipleSequentialTransactions();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testUpdateInTransaction(int useThrift, String backend) throws Exception {
init(useThrift);
super.testUpdateInTransaction();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testDeleteInTransaction(int useThrift, String backend) throws Exception {
init(useThrift);
super.testDeleteInTransaction();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testMultiTableCommit(int useThrift, String backend) throws Exception {
init(useThrift);
super.testMultiTableCommit();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testMultiTableRollback(int useThrift, String backend) throws Exception {
init(useThrift);
super.testMultiTableRollback();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testMultiTableAtomicity(int useThrift, String backend) throws Exception {
init(useThrift);
super.testMultiTableAtomicity();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testCrossTableMerge(int useThrift, String backend) throws Exception {
init(useThrift);
super.testCrossTableMerge();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testRepeatableReads(int useThrift, String backend) throws Exception {
init(useThrift);
super.testRepeatableReads();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testWriteConflictSingleTable(int useThrift, String backend) throws Exception {
init(useThrift);
super.testWriteConflictSingleTable();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testWriteSkewProvesSnapshotIsolation(int useThrift, String backend) throws Exception {
init(useThrift);
super.testWriteSkewProvesSnapshotIsolation();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testCommitWithoutActiveTxnThrows(int useThrift, String backend) throws Exception {
init(useThrift);
super.testCommitWithoutActiveTxnThrows();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testRollbackWithoutActiveTxnBehavior(int useThrift, String backend) throws Exception {
init(useThrift);
// Explicit SQL ROLLBACK without active txn is a safe no-op
assertDoesNotThrow(() -> rollbackTransaction(connection));
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testCloseConnectionImplicitRollback(int useThrift, String backend) throws Exception {
init(useThrift);
super.testCloseConnectionImplicitRollback();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testCloseConnectionDoesNotThrow(int useThrift, String backend) throws Exception {
init(useThrift);
super.testCloseConnectionDoesNotThrow();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testEmptyTransactionRollback(int useThrift, String backend) throws Exception {
init(useThrift);
super.testEmptyTransactionRollback();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testReadOnlyTransaction(int useThrift, String backend) throws Exception {
init(useThrift);
super.testReadOnlyTransaction();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testRollbackAfterQueryFailure(int useThrift, String backend) throws Exception {
init(useThrift);
super.testRollbackAfterQueryFailure();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testMultipleStatementsInSingleTxn(int useThrift, String backend) throws Exception {
init(useThrift);
super.testMultipleStatementsInSingleTxn();
}

@ParameterizedTest(name = "{1}")
@MethodSource("backends")
void testPreparedStatementInsert(int useThrift, String backend) throws Exception {
init(useThrift);
super.testPreparedStatementInsert();
}

// ========================== SQL-SPECIFIC TESTS ==========================

@ParameterizedTest(name = "C.1 nestedBeginFails [{1}]")
@MethodSource("backends")
void testNestedBeginTransactionFails(int useThrift, String backend) throws SQLException {
init(useThrift);
executeSql(connection, "BEGIN TRANSACTION");
assertThrows(SQLException.class, () -> executeSql(connection, "BEGIN TRANSACTION"));
executeSql(connection, "ROLLBACK");
}

@ParameterizedTest(name = "C.2 beginFailsWhenAutocommitFalse [{1}]")
@MethodSource("backends")
void testBeginFailsWhenAutocommitFalse(int useThrift, String backend) throws SQLException {
init(useThrift);
executeSql(connection, "SET AUTOCOMMIT = FALSE");
assertThrows(SQLException.class, () -> executeSql(connection, "BEGIN TRANSACTION"));
executeSql(connection, "ROLLBACK");
executeSql(connection, "SET AUTOCOMMIT = TRUE");
}

@ParameterizedTest(name = "C.3 setAutocommitFalseCommit [{1}]")
@MethodSource("backends")
void testSetAutocommitFalseCommit(int useThrift, String backend) throws SQLException {
init(useThrift);
String fqTable = getFullyQualifiedTableName();

executeSql(connection, "SET AUTOCOMMIT = FALSE");
executeSql(connection, "INSERT INTO " + fqTable + " VALUES (1, 'implicit_txn')");
executeSql(connection, "COMMIT");

assertEquals(1, getRowCountFromSeparateConnection(fqTable));
}

@ParameterizedTest(name = "C.4 setAutocommitFalseRollback [{1}]")
@MethodSource("backends")
void testSetAutocommitFalseRollback(int useThrift, String backend) throws SQLException {
init(useThrift);
String fqTable = getFullyQualifiedTableName();

executeSql(connection, "SET AUTOCOMMIT = FALSE");
executeSql(connection, "INSERT INTO " + fqTable + " VALUES (1, 'rolled_back')");
executeSql(connection, "ROLLBACK");

assertEquals(0, getRowCountFromSeparateConnection(fqTable));
}

@ParameterizedTest(name = "C.5 setAutocommitTrue [{1}]")
@MethodSource("backends")
void testSetAutocommitTrue(int useThrift, String backend) throws SQLException {
init(useThrift);
String fqTable = getFullyQualifiedTableName();

executeSql(connection, "SET AUTOCOMMIT = FALSE");
executeSql(connection, "INSERT INTO " + fqTable + " VALUES (1, 'committed')");
executeSql(connection, "COMMIT");
executeSql(connection, "SET AUTOCOMMIT = TRUE");
// This INSERT should auto-commit
executeSql(connection, "INSERT INTO " + fqTable + " VALUES (2, 'auto_committed')");

assertEquals(2, getRowCountFromSeparateConnection(fqTable));
}

@ParameterizedTest(name = "C.6 setAutocommitWithoutValue [{1}]")
@MethodSource("backends")
void testSetAutocommitWithoutValue(int useThrift, String backend) throws SQLException {
init(useThrift);
try (ResultSet rs1 = executeQuery(connection, "SET AUTOCOMMIT")) {
assertTrue(rs1.next());
String value1 = rs1.getString(1);
assertNotNull(value1);
}

executeSql(connection, "SET AUTOCOMMIT = FALSE");
try (ResultSet rs2 = executeQuery(connection, "SET AUTOCOMMIT")) {
assertTrue(rs2.next());
String value2 = rs2.getString(1);
assertNotNull(value2);
}

executeSql(connection, "ROLLBACK");
executeSql(connection, "SET AUTOCOMMIT = TRUE");
}

@ParameterizedTest(name = "C.7 setAutocommitTrueDuringActiveTxn [{1}]")
@MethodSource("backends")
void testSetAutocommitTrueDuringActiveTxnFails(int useThrift, String backend)
throws SQLException {
init(useThrift);
String fqTable = getFullyQualifiedTableName();

executeSql(connection, "SET AUTOCOMMIT = FALSE");
executeSql(connection, "INSERT INTO " + fqTable + " VALUES (1, 'active_txn')");
assertThrows(SQLException.class, () -> executeSql(connection, "SET AUTOCOMMIT = TRUE"));
executeSql(connection, "ROLLBACK");
executeSql(connection, "SET AUTOCOMMIT = TRUE");
}
}
Loading
Loading