Skip to content
Permalink
Browse files

fix: map integrity constraint violation to XA_RBINTEGRITY instead of …

…XAER_RMFAIL (#1175)

fixes #1171
  • Loading branch information...
janvdbergh authored and vlsi committed Jul 14, 2018
1 parent 4668f43 commit f2d1352c2b3ea98492beb6127cd6d95039a0b92f
@@ -357,7 +357,7 @@ public int prepare(Xid xid) throws XAException {

return XA_OK;
} catch (SQLException ex) {
throw new PGXAException(GT.tr("Error preparing transaction. prepare xid={0}", xid), ex, XAException.XAER_RMERR);
throw new PGXAException(GT.tr("Error preparing transaction. prepare xid={0}", xid), ex, mapSQLStateToXAErrorCode(ex));
}
}

@@ -545,7 +545,7 @@ private void commitOnePhase(Xid xid) throws XAException {
conn.commit();
conn.setAutoCommit(localAutoCommitMode);
} catch (SQLException ex) {
throw new PGXAException(GT.tr("Error during one-phase commit. commit xid={0}", xid), ex, XAException.XAER_RMFAIL);
throw new PGXAException(GT.tr("Error during one-phase commit. commit xid={0}", xid), ex, mapSQLStateToXAErrorCode(ex));
}
}

@@ -643,6 +643,20 @@ public boolean setTransactionTimeout(int seconds) {
return false;
}

private int mapSQLStateToXAErrorCode(SQLException sqlException) {
if (isPostgreSQLIntegrityConstraintViolation(sqlException)) {
return XAException.XA_RBINTEGRITY;
}

return XAException.XAER_RMFAIL;
}

private boolean isPostgreSQLIntegrityConstraintViolation(SQLException sqlException) {
return sqlException instanceof PSQLException
&& sqlException.getSQLState().length() == 5
&& sqlException.getSQLState().startsWith("23"); // Class 23 - Integrity Constraint Violation
}

private enum State {
/**
* {@code PGXAConnection} not associated with a XA-transaction. You can still call {@link #getConnection()} and
@@ -23,13 +23,11 @@
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import java.util.Arrays;
import java.util.Random;

import javax.sql.XAConnection;
import javax.sql.XADataSource;

import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
@@ -66,6 +64,8 @@ public void setUp() throws Exception {
st.close();

TestUtil.createTable(_conn, "testxa1", "foo int");
TestUtil.createTable(_conn, "testxa2", "foo int primary key");
TestUtil.createTable(_conn, "testxa3", "foo int references testxa2(foo) deferrable");

clearAllPrepared();

@@ -92,6 +92,8 @@ public void tearDown() throws SQLException {
}

clearAllPrepared();
TestUtil.dropTable(_conn, "testxa3");
TestUtil.dropTable(_conn, "testxa2");
TestUtil.dropTable(_conn, "testxa1");
TestUtil.closeDB(_conn);

@@ -780,6 +782,28 @@ public void testNetworkIssueOnRollback() throws Exception {
}
}

/**
* When using deferred constraints a contraint violation can occur on prepare. This has to be
* mapped to the correct XA Error Code
*/
@Test
public void testMappingOfConstraintViolations() throws Exception {
Xid xid = new CustomXid(1);
xaRes.start(xid, XAResource.TMNOFLAGS);
assertEquals(0, conn.createStatement().executeUpdate("SET CONSTRAINTS ALL DEFERRED"));
assertEquals(1, conn.createStatement().executeUpdate("INSERT INTO testxa3 VALUES (4)"));
xaRes.end(xid, XAResource.TMSUCCESS);

try {
xaRes.prepare(xid);

fail("Prepare is expected to fail as an integrity violation occurred");
} catch (XAException xae) {
assertEquals("Prepare call with deferred constraints violations expects XA_RBINTEGRITY",
XAException.XA_RBINTEGRITY, xae.errorCode);
}
}

/*
* We don't support transaction interleaving. public void testInterleaving1() throws Exception {
* Xid xid1 = new CustomXid(1); Xid xid2 = new CustomXid(2);

0 comments on commit f2d1352

Please sign in to comment.
You can’t perform that action at this time.