Added:
branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXAException.java
branches/branch_5_1/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXAException.java
Modified:
branches/branch_5_0/connector-j/src/com/mysql/jdbc/Connection.java
branches/branch_5_0/connector-j/src/com/mysql/jdbc/MysqlErrorNumbers.java
branches/branch_5_0/connector-j/src/com/mysql/jdbc/ReplicationConnection.java
branches/branch_5_0/connector-j/src/com/mysql/jdbc/SQLError.java
branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java
branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java
branches/branch_5_0/connector-j/src/testsuite/simple/XATest.java
branches/branch_5_1/connector-j/CHANGES
branches/branch_5_1/connector-j/src/com/mysql/jdbc/Connection.java
branches/branch_5_1/connector-j/src/com/mysql/jdbc/MysqlErrorNumbers.java
branches/branch_5_1/connector-j/src/com/mysql/jdbc/SQLError.java
branches/branch_5_1/connector-j/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java
branches/branch_5_1/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java
branches/branch_5_1/connector-j/src/testsuite/simple/XATest.java
Log:
Fixed BUG#17401 - Can't use XAConnection for local transactions when
no global transaction is in progress.
Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/Connection.java
===================================================================
--- branches/branch_5_0/connector-j/src/com/mysql/jdbc/Connection.java 2006-02-15 01:01:48 UTC (rev 4935)
+++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/Connection.java 2006-02-15 22:06:32 UTC (rev 4936)
@@ -1231,6 +1231,9 @@
/** Has this connection been closed? */
private boolean isClosed = true;
+ /** Is this connection associated with a global tx? */
+ private boolean isInGlobalTx = false;
+
/** Is this connection running inside a JDK-1.3 VM? */
private boolean isRunningOnJDK13 = false;
@@ -5445,4 +5448,12 @@
protected boolean isRunningOnJDK13() {
return this.isRunningOnJDK13;
}
+
+ public boolean isInGlobalTx() {
+ return this.isInGlobalTx;
+ }
+
+ public void setInGlobalTx(boolean flag) {
+ this.isInGlobalTx = flag;
+ }
}
Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/MysqlErrorNumbers.java
===================================================================
--- branches/branch_5_0/connector-j/src/com/mysql/jdbc/MysqlErrorNumbers.java 2006-02-15 01:01:48 UTC (rev 4935)
+++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/MysqlErrorNumbers.java 2006-02-15 22:06:32 UTC (rev 4936)
@@ -624,6 +624,8 @@
public final static int ER_WRONG_VALUE_COUNT_ON_ROW = 1136;
public final static int ER_WRONG_VALUE_FOR_VAR = 1231;
+
+ public final static int ER_XA_RMERR = 1401;
public final static int ER_YES = 1003;
Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/ReplicationConnection.java
===================================================================
--- branches/branch_5_0/connector-j/src/com/mysql/jdbc/ReplicationConnection.java 2006-02-15 01:01:48 UTC (rev 4935)
+++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/ReplicationConnection.java 2006-02-15 22:06:32 UTC (rev 4936)
@@ -379,9 +379,13 @@
*/
public synchronized void setReadOnly(boolean readOnly) throws SQLException {
if (readOnly) {
- switchToSlavesConnection();
+ if (currentConnection != slavesConnection) {
+ switchToSlavesConnection();
+ }
} else {
- switchToMasterConnection();
+ if (currentConnection != masterConnection) {
+ switchToMasterConnection();
+ }
}
}
@@ -425,60 +429,51 @@
}
private synchronized void switchToMasterConnection() throws SQLException {
- String slaveCatalog = this.slavesConnection.getCatalog();
- String masterCatalog = this.masterConnection.getCatalog();
-
- if (slaveCatalog != null && !slaveCatalog.equals(masterCatalog)) {
- this.masterConnection.setCatalog(slaveCatalog);
- } else if (masterCatalog != null) {
- this.masterConnection.setCatalog(masterCatalog);
- }
-
- boolean slavesAutoCommit = this.slavesConnection.getAutoCommit();
-
- if (this.masterConnection.getAutoCommit() != slavesAutoCommit) {
- this.masterConnection.setAutoCommit(slavesAutoCommit);
- }
-
- int slavesTransactionIsolation = this.slavesConnection
- .getTransactionIsolation();
-
- if (this.masterConnection.getTransactionIsolation() != slavesTransactionIsolation) {
- this.masterConnection
- .setTransactionIsolation(slavesTransactionIsolation);
- }
-
- this.currentConnection = this.masterConnection;
+ swapConnections(this.masterConnection, this.slavesConnection);
}
private synchronized void switchToSlavesConnection() throws SQLException {
- String slaveCatalog = this.slavesConnection.getCatalog();
- String masterCatalog = this.masterConnection.getCatalog();
+ swapConnections(this.slavesConnection, this.masterConnection);
+ }
+
+ /**
+ * Swaps current context (catalog, autocommit and txn_isolation) from
+ * sourceConnection to targetConnection, and makes targetConnection
+ * the "current" connection that will be used for queries.
+ *
+ * @param switchToConnection the connection to swap from
+ * @param switchFromConnection the connection to swap to
+ *
+ * @throws SQLException if an error occurs
+ */
+ private synchronized void swapConnections(Connection switchToConnection,
+ Connection switchFromConnection) throws SQLException {
+ String switchFromCatalog = switchFromConnection.getCatalog();
+ String switchToCatalog = switchToConnection.getCatalog();
- if (masterCatalog != null && !masterCatalog.equals(slaveCatalog)) {
- this.slavesConnection.setCatalog(masterCatalog);
- } else if (slaveCatalog != null) {
- this.slavesConnection.setCatalog(slaveCatalog);
+ if (switchToCatalog != null && !switchToCatalog.equals(switchFromCatalog)) {
+ switchToConnection.setCatalog(switchFromCatalog);
+ } else if (switchFromCatalog != null) {
+ switchToConnection.setCatalog(switchFromCatalog);
}
- boolean masterAutoCommit = this.masterConnection.getAutoCommit();
-
- if (this.slavesConnection.getAutoCommit() != masterAutoCommit) {
- this.slavesConnection.setAutoCommit(masterAutoCommit);
+ boolean switchToAutoCommit = switchToConnection.getAutoCommit();
+ boolean switchFromConnectionAutoCommit = switchFromConnection.getAutoCommit();
+
+ if (switchFromConnectionAutoCommit != switchToAutoCommit) {
+ switchToConnection.setAutoCommit(switchFromConnectionAutoCommit);
}
- int masterTransactionIsolation = this.masterConnection
+ int switchToIsolation = switchToConnection
.getTransactionIsolation();
- if (this.slavesConnection.getTransactionIsolation() != masterTransactionIsolation) {
- this.slavesConnection
- .setTransactionIsolation(masterTransactionIsolation);
+ int switchFromIsolation = switchFromConnection.getTransactionIsolation();
+
+ if (switchFromIsolation != switchToIsolation) {
+ switchToConnection
+ .setTransactionIsolation(switchFromIsolation);
}
- this.currentConnection = this.slavesConnection;
-
- this.slavesConnection.setAutoCommit(this.masterConnection
- .getAutoCommit());
- this.slavesConnection.setTransactionIsolation(this.masterConnection
- .getTransactionIsolation());
+
+ this.currentConnection = switchToConnection;
}
}
Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/SQLError.java
===================================================================
--- branches/branch_5_0/connector-j/src/com/mysql/jdbc/SQLError.java 2006-02-15 01:01:48 UTC (rev 4935)
+++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/SQLError.java 2006-02-15 22:06:32 UTC (rev 4936)
@@ -127,6 +127,8 @@
public static final String SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE = "08001"; //$NON-NLS-1$
public static final String SQL_STATE_WRONG_NO_OF_PARAMETERS = "07001"; //$NON-NLS-1$
+
+ public static final String SQL_STATE_INVALID_TRANSACTION_TERMINATION = "2D000"; //$NON_NLS-1$
private static Map sqlStateMessages;
Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java
===================================================================
--- branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java 2006-02-15 01:01:48 UTC (rev 4935)
+++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java 2006-02-15 22:06:32 UTC (rev 4936)
@@ -29,6 +29,7 @@
import java.sql.Savepoint;
import java.sql.Statement;
+import com.mysql.jdbc.MysqlErrorNumbers;
import com.mysql.jdbc.SQLError;
/**
@@ -62,7 +63,7 @@
private boolean closed;
private boolean isForXa;
-
+
/**
* Construct a new LogicalHandle and set instance variables
*
@@ -84,6 +85,7 @@
this.isForXa = forXa;
if (this.isForXa) {
+ setInGlobalTx(false);
setAutoCommit(false);
}
}
@@ -97,9 +99,10 @@
public void setAutoCommit(boolean autoCommit) throws SQLException {
checkClosed();
- if (autoCommit && this.isForXa) {
+ if (autoCommit && isInGlobalTx()) {
throw SQLError.createSQLException("Can't set autocommit to 'true' on an XAConnection",
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+ SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION,
+ MysqlErrorNumbers.ER_XA_RMERR);
}
try {
@@ -277,6 +280,12 @@
public java.sql.Savepoint setSavepoint() throws SQLException {
checkClosed();
+ if (isInGlobalTx()) {
+ throw SQLError.createSQLException("Can't set autocommit to 'true' on an XAConnection",
+ SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION,
+ MysqlErrorNumbers.ER_XA_RMERR);
+ }
+
try {
return this.mc.setSavepoint();
} catch (SQLException sqlException) {
@@ -292,6 +301,12 @@
public java.sql.Savepoint setSavepoint(String arg0) throws SQLException {
checkClosed();
+ if (isInGlobalTx()) {
+ throw SQLError.createSQLException("Can't set autocommit to 'true' on an XAConnection",
+ SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION,
+ MysqlErrorNumbers.ER_XA_RMERR);
+ }
+
try {
return this.mc.setSavepoint(arg0);
} catch (SQLException sqlException) {
@@ -429,9 +444,11 @@
public void commit() throws SQLException {
checkClosed();
- if (this.isForXa) {
- throw SQLError.createSQLException("Can't call commit() on an XAConnection",
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+ if (isInGlobalTx()) {
+ throw SQLError.createSQLException(
+ "Can't call commit() on an XAConnection associated with a global transaction",
+ SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION,
+ MysqlErrorNumbers.ER_XA_RMERR);
}
try {
@@ -733,9 +750,10 @@
checkClosed();
- if (this.isForXa) {
- throw SQLError.createSQLException("Can't call rollback() on an XAConnection",
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+ if (isInGlobalTx()) {
+ throw SQLError.createSQLException("Can't call rollback() on an XAConnection associated with a global transaction",
+ SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION,
+ MysqlErrorNumbers.ER_XA_RMERR);
}
try {
@@ -751,9 +769,10 @@
public void rollback(Savepoint arg0) throws SQLException {
checkClosed();
- if (this.isForXa) {
- throw SQLError.createSQLException("Can't call rollback() on an XAConnection",
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+ if (isInGlobalTx()) {
+ throw SQLError.createSQLException("Can't call rollback() on an XAConnection associated with a global transaction",
+ SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION,
+ MysqlErrorNumbers.ER_XA_RMERR);
}
try {
@@ -769,7 +788,7 @@
return;
}
- if (!this.isForXa
+ if (!isInGlobalTx()
&& this.mc.getRollbackOnPooledClose()
&& !this.getAutoCommit()) {
rollback();
@@ -794,4 +813,12 @@
throw SQLError.createSQLException(this.invalidHandleStr);
}
}
+
+ protected boolean isInGlobalTx() {
+ return this.mc.isInGlobalTx();
+ }
+
+ protected void setInGlobalTx(boolean flag) {
+ this.mc.setInGlobalTx(flag);
+ }
}
Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java
===================================================================
--- branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java 2006-02-15 01:01:48 UTC (rev 4935)
+++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java 2006-02-15 22:06:32 UTC (rev 4936)
@@ -19,8 +19,6 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
*/
package com.mysql.jdbc.jdbc2.optional;
@@ -190,6 +188,10 @@
* XAER_RMFAIL, XAER_INVAL, and XAER_PROTO.
*/
public Xid[] recover(int flag) throws XAException {
+ return recover(this.underlyingConnection, flag);
+ }
+
+ protected static Xid[] recover(Connection c, int flag) throws XAException {
switch (flag) {
case TMSTARTRSCAN:
case TMENDRSCAN:
@@ -202,7 +204,7 @@
try {
// TODO: Cache this for lifetime of XAConnection
- stmt = this.underlyingConnection.createStatement();
+ stmt = c.createStatement();
rs = stmt.executeQuery("XA RECOVER");
@@ -329,7 +331,11 @@
commandBuf.append("XA ROLLBACK ");
commandBuf.append(xidToString(xid));
- dispatchCommand(commandBuf.toString());
+ try {
+ dispatchCommand(commandBuf.toString());
+ } finally {
+ this.underlyingConnection.setInGlobalTx(false);
+ }
}
/**
@@ -422,7 +428,8 @@
}
dispatchCommand(commandBuf.toString());
-
+
+ this.underlyingConnection.setInGlobalTx(true);
}
/**
@@ -455,7 +462,11 @@
commandBuf.append(" ONE PHASE");
}
- dispatchCommand(commandBuf.toString());
+ try {
+ dispatchCommand(commandBuf.toString());
+ } finally {
+ this.underlyingConnection.setInGlobalTx(false);
+ }
}
private ResultSet dispatchCommand(String command) throws XAException {
@@ -487,7 +498,7 @@
}
}
- private XAException mapXAExceptionFromSQLException(SQLException sqlEx) {
+ protected static XAException mapXAExceptionFromSQLException(SQLException sqlEx) {
Integer xaCode = (Integer) MYSQL_ERROR_CODES_TO_XA_ERROR_CODES
.get(new Integer(sqlEx.getErrorCode()));
@@ -568,44 +579,4 @@
return connToWrap;
}
-
- /**
- * The stock XAException class isn't too friendly (i.e. no
- * error messages), so we extend it a bit.
- */
- class MysqlXAException extends XAException {
- private static final long serialVersionUID = -9075817535836563004L;
-
- private String message;
- private String xidAsString;
-
- public MysqlXAException(int errorCode, String message, String xidAsString) {
- super(errorCode);
- this.message = message;
- this.xidAsString = xidAsString;
- }
-
- public MysqlXAException(String message, String xidAsString) {
- super();
-
- this.message = message;
- this.xidAsString = xidAsString;
- }
-
- public String getMessage() {
- String superMessage = super.getMessage();
- StringBuffer returnedMessage = new StringBuffer();
-
- if (superMessage != null) {
- returnedMessage.append(superMessage);
- returnedMessage.append(":");
- }
-
- if (this.message != null) {
- returnedMessage.append(this.message);
- }
-
- return returnedMessage.toString();
- }
- }
}
Added: branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXAException.java
===================================================================
--- branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXAException.java 2006-02-15 01:01:48 UTC (rev 4935)
+++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXAException.java 2006-02-15 22:06:32 UTC (rev 4936)
@@ -0,0 +1,66 @@
+/*
+ Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ */
+
+package com.mysql.jdbc.jdbc2.optional;
+
+import javax.transaction.xa.XAException;
+
+/**
+ * The stock XAException class isn't too friendly (i.e. no
+ * error messages), so we extend it a bit.
+ */
+class MysqlXAException extends XAException {
+ private static final long serialVersionUID = -9075817535836563004L;
+
+ private String message;
+ private String xidAsString;
+
+ public MysqlXAException(int errorCode, String message, String xidAsString) {
+ super(errorCode);
+ this.message = message;
+ this.xidAsString = xidAsString;
+ }
+
+ public MysqlXAException(String message, String xidAsString) {
+ super();
+
+ this.message = message;
+ this.xidAsString = xidAsString;
+ }
+
+ public String getMessage() {
+ String superMessage = super.getMessage();
+ StringBuffer returnedMessage = new StringBuffer();
+
+ if (superMessage != null) {
+ returnedMessage.append(superMessage);
+ returnedMessage.append(":");
+ }
+
+ if (this.message != null) {
+ returnedMessage.append(this.message);
+ }
+
+ return returnedMessage.toString();
+ }
+}
\ No newline at end of file
Modified: branches/branch_5_0/connector-j/src/testsuite/simple/XATest.java
===================================================================
--- branches/branch_5_0/connector-j/src/testsuite/simple/XATest.java 2006-02-15 01:01:48 UTC (rev 4935)
+++ branches/branch_5_0/connector-j/src/testsuite/simple/XATest.java 2006-02-15 22:06:32 UTC (rev 4936)
@@ -28,6 +28,8 @@
import java.io.IOException;
import java.rmi.server.UID;
import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Savepoint;
import javax.sql.XAConnection;
import javax.transaction.xa.XAResource;
@@ -40,7 +42,7 @@
/**
* Unit tests for our XA implementation.
- *
+ *
* @version $Id: $
*/
public class XATest extends BaseTestCase {
@@ -55,10 +57,10 @@
}
/**
- * Tests that simple distributed transaction processing
- * works as expected.
+ * Tests that simple distributed transaction processing works as expected.
*
- * @throws Exception if the test fails.
+ * @throws Exception
+ * if the test fails.
*/
public void testCoordination() throws Exception {
if (!versionMeetsMinimum(5, 0)) {
@@ -91,6 +93,11 @@
xaRes1.end(xid1, XAResource.TMSUCCESS);
xaRes2.end(xid2, XAResource.TMSUCCESS);
+ if (true) {
+ xaRes1.start(xid1, XAResource.TMJOIN);
+ xaRes2.start(xid2, XAResource.TMJOIN);
+ }
+
xaRes1.prepare(xid1);
xaRes2.prepare(xid2);
@@ -123,12 +130,17 @@
conn2.createStatement().executeUpdate("INSERT INTO testCoordination VALUES (2)");
- // ensure visibility
+ // ensure visibility
assertEquals("2", getSingleIndexedValueWithQuery(conn2, 1, "SELECT field1 FROM testCoordination WHERE field1=2").toString());
xaRes1.end(xid1, XAResource.TMSUCCESS);
xaRes2.end(xid2, XAResource.TMSUCCESS);
+ if (true) {
+ xaRes1.start(xid1, XAResource.TMJOIN);
+ xaRes2.start(xid2, XAResource.TMJOIN);
+ }
+
xaRes1.prepare(xid1);
xaRes2.prepare(xid2);
@@ -164,7 +176,8 @@
/**
* Tests that XA RECOVER works as expected.
*
- * @throws Exception if test fails
+ * @throws Exception
+ * if test fails
*/
public void testRecover() throws Exception {
if (!versionMeetsMinimum(5, 0)) {
@@ -218,6 +231,123 @@
}
}
+ /**
+ * Tests operation of local transactions on XAConnections when global
+ * transactions are in or not in progress (follows from BUG#17401).
+ *
+ * @throws Exception
+ * if the testcase fails
+ */
+ public void testLocalTransaction() throws Exception {
+
+ if (!versionMeetsMinimum(5, 0) || isRunningOnJdk131()) {
+ return;
+ }
+
+ createTable("testLocalTransaction", "(field1 int) ENGINE=InnoDB");
+
+ Connection conn1 = null;
+
+ XAConnection xaConn1 = null;
+
+ try {
+ xaConn1 = getXAConnection();
+ XAResource xaRes1 = xaConn1.getXAResource();
+ conn1 = xaConn1.getConnection();
+ assertEquals(false, conn1.getAutoCommit());
+ conn1.setAutoCommit(true);
+ conn1.createStatement().executeUpdate(
+ "INSERT INTO testLocalTransaction VALUES (1)");
+ assertEquals("1", getSingleIndexedValueWithQuery(conn1, 1,
+ "SELECT field1 FROM testLocalTransaction").toString());
+
+ conn1.createStatement().executeUpdate(
+ "TRUNCATE TABLE testLocalTransaction");
+ conn1.setAutoCommit(false);
+ conn1.createStatement().executeUpdate(
+ "INSERT INTO testLocalTransaction VALUES (2)");
+ assertEquals("2", getSingleIndexedValueWithQuery(conn1, 1,
+ "SELECT field1 FROM testLocalTransaction").toString());
+ conn1.rollback();
+ assertEquals(0, getRowCount("testLocalTransaction"));
+
+ conn1.createStatement().executeUpdate(
+ "INSERT INTO testLocalTransaction VALUES (3)");
+ assertEquals("3", getSingleIndexedValueWithQuery(conn1, 1,
+ "SELECT field1 FROM testLocalTransaction").toString());
+ conn1.commit();
+ assertEquals("3", getSingleIndexedValueWithQuery(conn1, 1,
+ "SELECT field1 FROM testLocalTransaction").toString());
+ conn1.commit();
+
+ Savepoint sp = conn1.setSavepoint();
+ conn1.rollback(sp);
+ sp = conn1.setSavepoint("abcd");
+ conn1.rollback(sp);
+ Savepoint spSaved = sp;
+
+ Xid xid = createXid();
+ xaRes1.start(xid, XAResource.TMNOFLAGS);
+
+ try {
+ try {
+ conn1.setAutoCommit(true);
+ } catch (SQLException sqlEx) {
+ // we expect an exception here
+ assertEquals("2D000", sqlEx.getSQLState());
+ }
+
+ try {
+ conn1.commit();
+ } catch (SQLException sqlEx) {
+ // we expect an exception here
+ assertEquals("2D000", sqlEx.getSQLState());
+ }
+
+ try {
+ conn1.rollback();
+ } catch (SQLException sqlEx) {
+ // we expect an exception here
+ assertEquals("2D000", sqlEx.getSQLState());
+ }
+
+ try {
+ sp = conn1.setSavepoint();
+ } catch (SQLException sqlEx) {
+ // we expect an exception here
+ assertEquals("2D000", sqlEx.getSQLState());
+ }
+
+ try {
+ conn1.rollback(spSaved);
+ } catch (SQLException sqlEx) {
+ // we expect an exception here
+ assertEquals("2D000", sqlEx.getSQLState());
+ }
+
+ try {
+ sp = conn1.setSavepoint("abcd");
+ } catch (SQLException sqlEx) {
+ // we expect an exception here
+ assertEquals("2D000", sqlEx.getSQLState());
+ }
+
+ try {
+ conn1.rollback(spSaved);
+ } catch (SQLException sqlEx) {
+ // we expect an exception here
+ assertEquals("2D000", sqlEx.getSQLState());
+ }
+ } finally {
+ xaRes1.forget(xid);
+ }
+ } finally {
+ if (xaConn1 != null) {
+ xaConn1.close();
+ }
+ }
+ }
+
private Xid createXid() throws IOException {
ByteArrayOutputStream gtridOut = new ByteArrayOutputStream();
DataOutputStream dataOut = new DataOutputStream(gtridOut);
Modified: branches/branch_5_1/connector-j/CHANGES
===================================================================
--- branches/branch_5_1/connector-j/CHANGES 2006-02-15 01:01:48 UTC (rev 4935)
+++ branches/branch_5_1/connector-j/CHANGES 2006-02-15 22:06:32 UTC (rev 4936)
@@ -1,6 +1,13 @@
# Changelog
# $Id$
+xx-xx-06 - Version 5.1.0-alpha
+
+xx-xx-06 - Version 5.0.1-beta
+
+ - Fixed BUG#17401 - Can't use XAConnection for local transactions when
+ no global transaction is in progress.
+
12-23-05 - Version 5.0.0-beta
- XADataSource implemented (ported from 3.2 branch which won't be
Modified: branches/branch_5_1/connector-j/src/com/mysql/jdbc/Connection.java
===================================================================
--- branches/branch_5_1/connector-j/src/com/mysql/jdbc/Connection.java 2006-02-15 01:01:48 UTC (rev 4935)
+++ branches/branch_5_1/connector-j/src/com/mysql/jdbc/Connection.java 2006-02-15 22:06:32 UTC (rev 4936)
@@ -358,6 +358,9 @@
/** Has this connection been closed? */
private boolean isClosed = true;
+ /** Is this connection associated with a global tx? */
+ private boolean isInGlobalTx = false;
+
/** Is this connection running inside a JDK-1.3 VM? */
private boolean isRunningOnJDK13 = false;
@@ -4618,4 +4621,12 @@
return this.io.versionMeetsMinimum(major, minor, subminor);
}
+
+ public boolean isInGlobalTx() {
+ return this.isInGlobalTx;
+ }
+
+ public void setInGlobalTx(boolean flag) {
+ this.isInGlobalTx = flag;
+ }
}
Modified: branches/branch_5_1/connector-j/src/com/mysql/jdbc/MysqlErrorNumbers.java
===================================================================
--- branches/branch_5_1/connector-j/src/com/mysql/jdbc/MysqlErrorNumbers.java 2006-02-15 01:01:48 UTC (rev 4935)
+++ branches/branch_5_1/connector-j/src/com/mysql/jdbc/MysqlErrorNumbers.java 2006-02-15 22:06:32 UTC (rev 4936)
@@ -625,6 +625,8 @@
public final static int ER_WRONG_VALUE_FOR_VAR = 1231;
+ public final static int ER_XA_RMERR = 1401;
+
public final static int ER_YES = 1003;
public final static int ER_ZLIB_Z_BUF_ERROR = 1258;
Modified: branches/branch_5_1/connector-j/src/com/mysql/jdbc/SQLError.java
===================================================================
--- branches/branch_5_1/connector-j/src/com/mysql/jdbc/SQLError.java 2006-02-15 01:01:48 UTC (rev 4935)
+++ branches/branch_5_1/connector-j/src/com/mysql/jdbc/SQLError.java 2006-02-15 22:06:32 UTC (rev 4936)
@@ -128,6 +128,8 @@
public static final String SQL_STATE_WRONG_NO_OF_PARAMETERS = "07001"; //$NON-NLS-1$
+ public static final String SQL_STATE_INVALID_TRANSACTION_TERMINATION = "2D000"; //$NON_NLS-1$
+
private static Map sqlStateMessages;
static {
Modified: branches/branch_5_1/connector-j/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java
===================================================================
--- branches/branch_5_1/connector-j/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java 2006-02-15 01:01:48 UTC (rev 4935)
+++ branches/branch_5_1/connector-j/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java 2006-02-15 22:06:32 UTC (rev 4936)
@@ -36,6 +36,7 @@
import java.sql.Statement;
import java.util.Properties;
+import com.mysql.jdbc.MysqlErrorNumbers;
import com.mysql.jdbc.SQLError;
import com.mysql.jdbc.ToBeImplementedException;
@@ -92,6 +93,7 @@
this.isForXa = forXa;
if (this.isForXa) {
+ setInGlobalTx(false);
setAutoCommit(false);
}
}
@@ -105,9 +107,10 @@
public void setAutoCommit(boolean autoCommit) throws SQLException {
checkClosed();
- if (autoCommit && this.isForXa) {
+ if (autoCommit && isInGlobalTx()) {
throw SQLError.createSQLException("Can't set autocommit to 'true' on an XAConnection",
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+ SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION,
+ MysqlErrorNumbers.ER_XA_RMERR);
}
try {
@@ -285,6 +288,12 @@
public java.sql.Savepoint setSavepoint() throws SQLException {
checkClosed();
+ if (isInGlobalTx()) {
+ throw SQLError.createSQLException("Can't set autocommit to 'true' on an XAConnection",
+ SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION,
+ MysqlErrorNumbers.ER_XA_RMERR);
+ }
+
try {
return this.mc.setSavepoint();
} catch (SQLException sqlException) {
@@ -300,6 +309,12 @@
public java.sql.Savepoint setSavepoint(String arg0) throws SQLException {
checkClosed();
+ if (isInGlobalTx()) {
+ throw SQLError.createSQLException("Can't set autocommit to 'true' on an XAConnection",
+ SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION,
+ MysqlErrorNumbers.ER_XA_RMERR);
+ }
+
try {
return this.mc.setSavepoint(arg0);
} catch (SQLException sqlException) {
@@ -437,9 +452,11 @@
public void commit() throws SQLException {
checkClosed();
- if (this.isForXa) {
- throw SQLError.createSQLException("Can't call commit() on an XAConnection",
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+ if (isInGlobalTx()) {
+ throw SQLError.createSQLException(
+ "Can't call commit() on an XAConnection associated with a global transaction",
+ SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION,
+ MysqlErrorNumbers.ER_XA_RMERR);
}
try {
@@ -741,9 +758,10 @@
checkClosed();
- if (this.isForXa) {
- throw SQLError.createSQLException("Can't call rollback() on an XAConnection",
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+ if (isInGlobalTx()) {
+ throw SQLError.createSQLException("Can't call rollback() on an XAConnection associated with a global transaction",
+ SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION,
+ MysqlErrorNumbers.ER_XA_RMERR);
}
try {
@@ -759,9 +777,10 @@
public void rollback(Savepoint arg0) throws SQLException {
checkClosed();
- if (this.isForXa) {
- throw SQLError.createSQLException("Can't call rollback() on an XAConnection",
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+ if (isInGlobalTx()) {
+ throw SQLError.createSQLException("Can't call rollback() on an XAConnection associated with a global transaction",
+ SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION,
+ MysqlErrorNumbers.ER_XA_RMERR);
}
try {
@@ -777,7 +796,7 @@
return;
}
- if (!this.isForXa
+ if (!isInGlobalTx()
&& this.mc.getRollbackOnPooledClose()
&& !this.getAutoCommit()) {
rollback();
@@ -803,6 +822,14 @@
}
}
+ protected boolean isInGlobalTx() {
+ return this.mc.isInGlobalTx();
+ }
+
+ protected void setInGlobalTx(boolean flag) {
+ this.mc.setInGlobalTx(flag);
+ }
+
public Clob createClob() throws SQLException {
throw new ToBeImplementedException();
}
Modified: branches/branch_5_1/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java
===================================================================
--- branches/branch_5_1/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java 2006-02-15 01:01:48 UTC (rev 4935)
+++ branches/branch_5_1/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java 2006-02-15 22:06:32 UTC (rev 4936)
@@ -19,8 +19,6 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
*/
package com.mysql.jdbc.jdbc2.optional;
@@ -190,6 +188,10 @@
* XAER_RMFAIL, XAER_INVAL, and XAER_PROTO.
*/
public Xid[] recover(int flag) throws XAException {
+ return recover(this.underlyingConnection, flag);
+ }
+
+ protected static Xid[] recover(Connection c, int flag) throws XAException {
switch (flag) {
case TMSTARTRSCAN:
case TMENDRSCAN:
@@ -202,7 +204,7 @@
try {
// TODO: Cache this for lifetime of XAConnection
- stmt = this.underlyingConnection.createStatement();
+ stmt = c.createStatement();
rs = stmt.executeQuery("XA RECOVER");
@@ -329,7 +331,11 @@
commandBuf.append("XA ROLLBACK ");
commandBuf.append(xidToString(xid));
- dispatchCommand(commandBuf.toString());
+ try {
+ dispatchCommand(commandBuf.toString());
+ } finally {
+ this.underlyingConnection.setInGlobalTx(false);
+ }
}
/**
@@ -422,7 +428,8 @@
}
dispatchCommand(commandBuf.toString());
-
+
+ this.underlyingConnection.setInGlobalTx(true);
}
/**
@@ -455,7 +462,11 @@
commandBuf.append(" ONE PHASE");
}
- dispatchCommand(commandBuf.toString());
+ try {
+ dispatchCommand(commandBuf.toString());
+ } finally {
+ this.underlyingConnection.setInGlobalTx(false);
+ }
}
private ResultSet dispatchCommand(String command) throws XAException {
@@ -487,7 +498,7 @@
}
}
- private XAException mapXAExceptionFromSQLException(SQLException sqlEx) {
+ protected static XAException mapXAExceptionFromSQLException(SQLException sqlEx) {
Integer xaCode = (Integer) MYSQL_ERROR_CODES_TO_XA_ERROR_CODES
.get(new Integer(sqlEx.getErrorCode()));
@@ -568,44 +579,4 @@
return connToWrap;
}
-
- /**
- * The stock XAException class isn't too friendly (i.e. no
- * error messages), so we extend it a bit.
- */
- class MysqlXAException extends XAException {
- private static final long serialVersionUID = -9075817535836563004L;
-
- private String message;
- private String xidAsString;
-
- public MysqlXAException(int errorCode, String message, String xidAsString) {
- super(errorCode);
- this.message = message;
- this.xidAsString = xidAsString;
- }
-
- public MysqlXAException(String message, String xidAsString) {
- super();
-
- this.message = message;
- this.xidAsString = xidAsString;
- }
-
- public String getMessage() {
- String superMessage = super.getMessage();
- StringBuffer returnedMessage = new StringBuffer();
-
- if (superMessage != null) {
- returnedMessage.append(superMessage);
- returnedMessage.append(":");
- }
-
- if (this.message != null) {
- returnedMessage.append(this.message);
- }
-
- return returnedMessage.toString();
- }
- }
}
Added: branches/branch_5_1/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXAException.java
===================================================================
--- branches/branch_5_1/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXAException.java 2006-02-15 01:01:48 UTC (rev 4935)
+++ branches/branch_5_1/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXAException.java 2006-02-15 22:06:32 UTC (rev 4936)
@@ -0,0 +1,66 @@
+/*
+ Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ */
+
+package com.mysql.jdbc.jdbc2.optional;
+
+import javax.transaction.xa.XAException;
+
+/**
+ * The stock XAException class isn't too friendly (i.e. no
+ * error messages), so we extend it a bit.
+ */
+class MysqlXAException extends XAException {
+ private static final long serialVersionUID = -9075817535836563004L;
+
+ private String message;
+ private String xidAsString;
+
+ public MysqlXAException(int errorCode, String message, String xidAsString) {
+ super(errorCode);
+ this.message = message;
+ this.xidAsString = xidAsString;
+ }
+
+ public MysqlXAException(String message, String xidAsString) {
+ super();
+
+ this.message = message;
+ this.xidAsString = xidAsString;
+ }
+
+ public String getMessage() {
+ String superMessage = super.getMessage();
+ StringBuffer returnedMessage = new StringBuffer();
+
+ if (superMessage != null) {
+ returnedMessage.append(superMessage);
+ returnedMessage.append(":");
+ }
+
+ if (this.message != null) {
+ returnedMessage.append(this.message);
+ }
+
+ return returnedMessage.toString();
+ }
+}
\ No newline at end of file
Modified: branches/branch_5_1/connector-j/src/testsuite/simple/XATest.java
===================================================================
--- branches/branch_5_1/connector-j/src/testsuite/simple/XATest.java 2006-02-15 01:01:48 UTC (rev 4935)
+++ branches/branch_5_1/connector-j/src/testsuite/simple/XATest.java 2006-02-15 22:06:32 UTC (rev 4936)
@@ -28,6 +28,8 @@
import java.io.IOException;
import java.rmi.server.UID;
import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Savepoint;
import javax.sql.XAConnection;
import javax.transaction.xa.XAResource;
@@ -40,7 +42,7 @@
/**
* Unit tests for our XA implementation.
- *
+ *
* @version $Id: $
*/
public class XATest extends BaseTestCase {
@@ -55,10 +57,10 @@
}
/**
- * Tests that simple distributed transaction processing
- * works as expected.
+ * Tests that simple distributed transaction processing works as expected.
*
- * @throws Exception if the test fails.
+ * @throws Exception
+ * if the test fails.
*/
public void testCoordination() throws Exception {
if (!versionMeetsMinimum(5, 0)) {
@@ -91,6 +93,11 @@
xaRes1.end(xid1, XAResource.TMSUCCESS);
xaRes2.end(xid2, XAResource.TMSUCCESS);
+ if (true) {
+ xaRes1.start(xid1, XAResource.TMJOIN);
+ xaRes2.start(xid2, XAResource.TMJOIN);
+ }
+
xaRes1.prepare(xid1);
xaRes2.prepare(xid2);
@@ -123,12 +130,17 @@
conn2.createStatement().executeUpdate("INSERT INTO testCoordination VALUES (2)");
- // ensure visibility
+ // ensure visibility
assertEquals("2", getSingleIndexedValueWithQuery(conn2, 1, "SELECT field1 FROM testCoordination WHERE field1=2").toString());
xaRes1.end(xid1, XAResource.TMSUCCESS);
xaRes2.end(xid2, XAResource.TMSUCCESS);
+ if (true) {
+ xaRes1.start(xid1, XAResource.TMJOIN);
+ xaRes2.start(xid2, XAResource.TMJOIN);
+ }
+
xaRes1.prepare(xid1);
xaRes2.prepare(xid2);
@@ -164,7 +176,8 @@
/**
* Tests that XA RECOVER works as expected.
*
- * @throws Exception if test fails
+ * @throws Exception
+ * if test fails
*/
public void testRecover() throws Exception {
if (!versionMeetsMinimum(5, 0)) {
@@ -218,6 +231,123 @@
}
}
+ /**
+ * Tests operation of local transactions on XAConnections when global
+ * transactions are in or not in progress (follows from BUG#17401).
+ *
+ * @throws Exception
+ * if the testcase fails
+ */
+ public void testLocalTransaction() throws Exception {
+
+ if (!versionMeetsMinimum(5, 0) || isRunningOnJdk131()) {
+ return;
+ }
+
+ createTable("testLocalTransaction", "(field1 int) ENGINE=InnoDB");
+
+ Connection conn1 = null;
+
+ XAConnection xaConn1 = null;
+
+ try {
+ xaConn1 = getXAConnection();
+ XAResource xaRes1 = xaConn1.getXAResource();
+ conn1 = xaConn1.getConnection();
+ assertEquals(false, conn1.getAutoCommit());
+ conn1.setAutoCommit(true);
+ conn1.createStatement().executeUpdate(
+ "INSERT INTO testLocalTransaction VALUES (1)");
+ assertEquals("1", getSingleIndexedValueWithQuery(conn1, 1,
+ "SELECT field1 FROM testLocalTransaction").toString());
+
+ conn1.createStatement().executeUpdate(
+ "TRUNCATE TABLE testLocalTransaction");
+ conn1.setAutoCommit(false);
+ conn1.createStatement().executeUpdate(
+ "INSERT INTO testLocalTransaction VALUES (2)");
+ assertEquals("2", getSingleIndexedValueWithQuery(conn1, 1,
+ "SELECT field1 FROM testLocalTransaction").toString());
+ conn1.rollback();
+ assertEquals(0, getRowCount("testLocalTransaction"));
+
+ conn1.createStatement().executeUpdate(
+ "INSERT INTO testLocalTransaction VALUES (3)");
+ assertEquals("3", getSingleIndexedValueWithQuery(conn1, 1,
+ "SELECT field1 FROM testLocalTransaction").toString());
+ conn1.commit();
+ assertEquals("3", getSingleIndexedValueWithQuery(conn1, 1,
+ "SELECT field1 FROM testLocalTransaction").toString());
+ conn1.commit();
+
+ Savepoint sp = conn1.setSavepoint();
+ conn1.rollback(sp);
+ sp = conn1.setSavepoint("abcd");
+ conn1.rollback(sp);
+ Savepoint spSaved = sp;
+
+ Xid xid = createXid();
+ xaRes1.start(xid, XAResource.TMNOFLAGS);
+
+ try {
+ try {
+ conn1.setAutoCommit(true);
+ } catch (SQLException sqlEx) {
+ // we expect an exception here
+ assertEquals("2D000", sqlEx.getSQLState());
+ }
+
+ try {
+ conn1.commit();
+ } catch (SQLException sqlEx) {
+ // we expect an exception here
+ assertEquals("2D000", sqlEx.getSQLState());
+ }
+
+ try {
+ conn1.rollback();
+ } catch (SQLException sqlEx) {
+ // we expect an exception here
+ assertEquals("2D000", sqlEx.getSQLState());
+ }
+
+ try {
+ sp = conn1.setSavepoint();
+ } catch (SQLException sqlEx) {
+ // we expect an exception here
+ assertEquals("2D000", sqlEx.getSQLState());
+ }
+
+ try {
+ conn1.rollback(spSaved);
+ } catch (SQLException sqlEx) {
+ // we expect an exception here
+ assertEquals("2D000", sqlEx.getSQLState());
+ }
+
+ try {
+ sp = conn1.setSavepoint("abcd");
+ } catch (SQLException sqlEx) {
+ // we expect an exception here
+ assertEquals("2D000", sqlEx.getSQLState());
+ }
+
+ try {
+ conn1.rollback(spSaved);
+ } catch (SQLException sqlEx) {
+ // we expect an exception here
+ assertEquals("2D000", sqlEx.getSQLState());
+ }
+ } finally {
+ xaRes1.forget(xid);
+ }
+ } finally {
+ if (xaConn1 != null) {
+ xaConn1.close();
+ }
+ }
+ }
+
private Xid createXid() throws IOException {
ByteArrayOutputStream gtridOut = new ByteArrayOutputStream();
DataOutputStream dataOut = new DataOutputStream(gtridOut);
| Thread |
|---|
| • Connector/J commit: r4936 - in branches: branch_5_0/connector-j/src/com/mysql/jdbc branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional branch_5_0... | mmatthews | 15 Feb |