use of org.firebirdsql.gds.impl.GDSHelper in project jaybird by FirebirdSQL.
the class FBManagedConnection method recover.
/**
* Obtain a list of prepared transaction branches from a resource manager.
* The transaction manager calls this method during recovery to obtain the
* list of transaction branches that are currently in prepared or
* heuristically completed states.
*
* @param flags
* One of TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS. TMNOFLAGS must be
* used when no other flags are set in flags.
* @return The resource manager returns zero or more XIDs for the
* transaction branches that are currently in a prepared or
* heuristically completed state. If an error occurs during the
* operation, the resource manager should throw the appropriate
* XAException.
* @throws XAException
* An error has occurred. Possible values are XAER_RMERR,
* XAER_RMFAIL, XAER_INVAL, and XAER_PROTO.
*/
public Xid[] recover(int flags) throws javax.transaction.xa.XAException {
if (flags != XAResource.TMSTARTRSCAN && flags != XAResource.TMENDRSCAN && flags != XAResource.TMNOFLAGS && flags != (XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN))
throw new FBXAException("flag not allowed in this context: " + flags + ", valid flags are TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS, TMSTARTRSCAN|TMENDRSCAN", XAException.XAER_PROTO);
try {
// if (!((flags & XAResource.TMSTARTRSCAN) == 0))
// if ((flags & XAResource.TMENDRSCAN) == 0 && (flags & XAResource.TMNOFLAGS) == 0)
// return new Xid[0];
List<FBXid> xids = new ArrayList<>();
FbTransaction trHandle2 = database.startTransaction(tpb.getTransactionParameterBuffer());
FbStatement stmtHandle2 = database.createStatement(trHandle2);
GDSHelper gdsHelper2 = new GDSHelper(database);
gdsHelper2.setCurrentTransaction(trHandle2);
stmtHandle2.prepare(RECOVERY_QUERY);
DataProvider dataProvider0 = new DataProvider(0);
stmtHandle2.addStatementListener(dataProvider0);
DataProvider dataProvider1 = new DataProvider(1);
stmtHandle2.addStatementListener(dataProvider1);
stmtHandle2.execute(RowValue.EMPTY_ROW_VALUE);
stmtHandle2.fetchRows(10);
FBField field0 = FBField.createField(stmtHandle2.getFieldDescriptor().getFieldDescriptor(0), dataProvider0, gdsHelper2, false);
FBField field1 = FBField.createField(stmtHandle2.getFieldDescriptor().getFieldDescriptor(1), dataProvider1, gdsHelper2, false);
int row = 0;
while (row < dataProvider0.getRowCount()) {
dataProvider0.setRow(row);
dataProvider1.setRow(row);
long inLimboTxId = field0.getLong();
byte[] inLimboMessage = field1.getBytes();
try {
FBXid xid = new FBXid(new ByteArrayInputStream(inLimboMessage), inLimboTxId);
xids.add(xid);
} catch (FBIncorrectXidException ex) {
log.warn("ignoring XID stored with invalid format in RDB$TRANSACTIONS for RDB$TRANSACTION_ID=" + inLimboTxId);
}
row++;
}
stmtHandle2.close();
trHandle2.commit();
return xids.toArray(new FBXid[0]);
} catch (SQLException | ResourceException e) {
throw new FBXAException("can't perform query to fetch xids", XAException.XAER_RMFAIL, e);
}
}
use of org.firebirdsql.gds.impl.GDSHelper in project jaybird by FirebirdSQL.
the class FBManagedConnectionFactory method tryCompleteInLimboTransaction.
/**
* Try to complete the "in limbo" transaction. This method tries to
* reconnect an "in limbo" transaction and complete it either by commit or
* rollback. If no "in limbo" transaction can be found, or error happens
* during completion, an exception is thrown.
*
* @param xid
* Xid of the transaction to reconnect.
* @param commit
* <code>true</code> if "in limbo" transaction should be
* committed, otherwise <code>false</code>.
*
* @throws XAException
* if "in limbo" transaction cannot be completed.
*/
private void tryCompleteInLimboTransaction(Xid xid, boolean commit) throws XAException {
try {
FBManagedConnection tempMc = null;
FirebirdLocalTransaction tempLocalTx = null;
try {
tempMc = new FBManagedConnection(null, null, this);
tempLocalTx = (FirebirdLocalTransaction) tempMc.getLocalTransaction();
tempLocalTx.begin();
long fbTransactionId = 0;
boolean found = false;
if (tempMc.getGDSHelper().compareToVersion(2, 0) < 0) {
// Find Xid by scanning
FBXid[] inLimboIds = (FBXid[]) tempMc.recover(XAResource.TMSTARTRSCAN);
for (FBXid inLimboId : inLimboIds) {
if (inLimboId.equals(xid)) {
found = true;
fbTransactionId = inLimboId.getFirebirdTransactionId();
}
}
} else {
// Find Xid by intelligent scan
FBXid foundXid = (FBXid) tempMc.findSingleXid(xid);
if (foundXid != null && foundXid.equals(xid)) {
found = true;
fbTransactionId = foundXid.getFirebirdTransactionId();
}
}
if (!found) {
throw new FBXAException((commit ? "Commit" : "Rollback") + " called with unknown transaction.", XAException.XAER_NOTA);
}
FbDatabase dbHandle = tempMc.getGDSHelper().getCurrentDatabase();
FbTransaction trHandle = dbHandle.reconnectTransaction(fbTransactionId);
// complete transaction by commit or rollback
if (commit) {
trHandle.commit();
} else {
trHandle.rollback();
}
if (tempMc.getGDSHelper().compareToVersion(3, 0) < 0) {
// remove heuristic data from rdb$transactions (only possible in versions before Firebird 3)
try {
String query = "delete from rdb$transactions where rdb$transaction_id = " + fbTransactionId;
GDSHelper gdsHelper = new GDSHelper(dbHandle);
FbTransaction trHandle2 = dbHandle.startTransaction(getDefaultTpb().getTransactionParameterBuffer());
gdsHelper.setCurrentTransaction(trHandle2);
FbStatement stmtHandle2 = dbHandle.createStatement(trHandle2);
stmtHandle2.prepare(query);
stmtHandle2.execute(RowValue.EMPTY_ROW_VALUE);
stmtHandle2.close();
trHandle2.commit();
} catch (SQLException sqle) {
throw new FBXAException("unable to remove in limbo transaction from rdb$transactions where rdb$transaction_id = " + fbTransactionId, XAException.XAER_RMERR);
}
}
} catch (SQLException ex) {
/*
* if ex.getIntParam() is 335544353 (transaction is not in limbo) and next ex.getIntParam() is 335544468 (transaction {0} is {1})
* => detected heuristic
*/
// TODO: We may need to parse the exception to get the details (or we need to handle this specific one differently)
int errorCode = XAException.XAER_RMERR;
int sqlError = ex.getErrorCode();
if (sqlError == ISCConstants.isc_no_recon) /*&& nextIntParam == ISCConstants.isc_tra_state*/
{
if (ex.getMessage().contains("committed")) {
errorCode = XAException.XA_HEURCOM;
} else if (ex.getMessage().contains("rolled back")) {
errorCode = XAException.XA_HEURCOM;
}
}
throw new FBXAException("unable to complete in limbo transaction", errorCode, ex);
} finally {
try {
if (tempLocalTx != null && tempLocalTx.inTransaction())
tempLocalTx.commit();
} finally {
if (tempMc != null)
tempMc.destroy();
}
}
} catch (ResourceException ex) {
throw new FBXAException(XAException.XAER_RMERR, ex);
}
}
use of org.firebirdsql.gds.impl.GDSHelper in project jaybird by FirebirdSQL.
the class TestReconnectTransaction method testReconnectTransaction.
@Test
public void testReconnectTransaction() throws Exception {
FbConnectionProperties connectionInfo = new FbConnectionProperties();
connectionInfo.setServerName(FBTestProperties.DB_SERVER_URL);
connectionInfo.setPortNumber(FBTestProperties.DB_SERVER_PORT);
connectionInfo.setUser(DB_USER);
connectionInfo.setPassword(DB_PASSWORD);
connectionInfo.setDatabaseName(FBTestProperties.getDatabasePath());
connectionInfo.setEncoding("NONE");
FbDatabaseFactory databaseFactory = FBTestProperties.getFbDatabaseFactory();
try (FbDatabase dbHandle1 = databaseFactory.connect(connectionInfo)) {
dbHandle1.attach();
FbTransaction trHandle1 = dbHandle1.startTransaction(tpb.getTransactionParameterBuffer());
trHandle1.prepare(message);
// No commit! We leave trHandle1 in Limbo.
}
try (FbDatabase dbHandle2 = databaseFactory.connect(connectionInfo)) {
dbHandle2.attach();
GDSHelper gdsHelper2 = new GDSHelper(dbHandle2);
FbTransaction trHandle2 = dbHandle2.startTransaction(tpb.getTransactionParameterBuffer());
gdsHelper2.setCurrentTransaction(trHandle2);
FbStatement stmtHandle2 = dbHandle2.createStatement(trHandle2);
stmtHandle2.prepare(RECOVERY_QUERY);
final List<RowValue> rows = new ArrayList<>();
StatementListener stmtListener = new DefaultStatementListener() {
@Override
public void receivedRow(FbStatement sender, RowValue rowValues) {
rows.add(rowValues);
}
};
stmtHandle2.addStatementListener(stmtListener);
stmtHandle2.execute(RowValue.EMPTY_ROW_VALUE);
stmtHandle2.fetchRows(10);
DataProvider dataProvider0 = new DataProvider(rows, 0);
DataProvider dataProvider1 = new DataProvider(rows, 1);
FBField field0 = FBField.createField(stmtHandle2.getRowDescriptor().getFieldDescriptor(0), dataProvider0, gdsHelper2, false);
FBField field1 = FBField.createField(stmtHandle2.getRowDescriptor().getFieldDescriptor(1), dataProvider1, gdsHelper2, false);
boolean foundInLimboTx = false;
int row = 0;
while (row < rows.size()) {
dataProvider0.setRow(row);
dataProvider1.setRow(row);
long inLimboTxId = field0.getLong();
byte[] inLimboMessage = field1.getBytes();
if (Arrays.equals(message, inLimboMessage)) {
foundInLimboTx = true;
FbTransaction inLimboTrHandle = dbHandle2.reconnectTransaction(inLimboTxId);
assertEquals(inLimboTxId, inLimboTrHandle.getTransactionId());
inLimboTrHandle.rollback();
break;
}
row++;
}
stmtHandle2.close();
trHandle2.commit();
assertTrue("Should find in-limbo tx.", foundInLimboTx);
}
}
use of org.firebirdsql.gds.impl.GDSHelper in project jaybird by FirebirdSQL.
the class TestBackupManager method testRestorePageSize16384.
/**
* Test if restoring a database to page size 16384 works.
*/
@Test
public void testRestorePageSize16384() throws Exception {
usesDatabase.createDefaultDatabase();
backupManager.backupDatabase();
backupManager.setRestoreReplace(true);
backupManager.setRestorePageSize(PageSizeConstants.SIZE_16K);
backupManager.restoreDatabase();
try (Connection con = getConnectionViaDriverManager()) {
GDSHelper gdsHelper = ((FBConnection) con).getGDSHelper();
final FbDatabase currentDatabase = gdsHelper.getCurrentDatabase();
final byte[] databaseInfo = currentDatabase.getDatabaseInfo(new byte[] { ISCConstants.isc_info_page_size }, 10);
assertEquals("Unexpected info item", ISCConstants.isc_info_page_size, databaseInfo[0]);
int length = iscVaxInteger2(databaseInfo, 1);
int pageSize = iscVaxInteger(databaseInfo, 3, length);
assertEquals("Unexpected page size", 16384, pageSize);
}
}
use of org.firebirdsql.gds.impl.GDSHelper in project jaybird by FirebirdSQL.
the class FBManagedConnection method findSingleXid.
/**
* Obtain a single prepared transaction branch from a resource manager, based on a Xid
*
* @param externalXid
* The Xid to find
* @return The Xid if found, otherwise null.
* @throws XAException
* An error has occurred. Possible values are XAER_RMERR,
* XAER_RMFAIL, XAER_INVAL, and XAER_PROTO.
*/
protected Xid findSingleXid(Xid externalXid) throws javax.transaction.xa.XAException {
try {
FbTransaction trHandle2 = database.startTransaction(tpb.getTransactionParameterBuffer());
FbStatement stmtHandle2 = database.createStatement(trHandle2);
GDSHelper gdsHelper2 = new GDSHelper(database);
gdsHelper2.setCurrentTransaction(trHandle2);
stmtHandle2.prepare(RECOVERY_QUERY_PARAMETRIZED);
DataProvider dataProvider0 = new DataProvider(0);
stmtHandle2.addStatementListener(dataProvider0);
DataProvider dataProvider1 = new DataProvider(1);
stmtHandle2.addStatementListener(dataProvider1);
FBXid tempXid = new FBXid(externalXid);
final RowValue parameters = RowValue.of(stmtHandle2.getParameterDescriptor(), tempXid.toBytes());
stmtHandle2.execute(parameters);
stmtHandle2.fetchRows(1);
FBField field0 = FBField.createField(stmtHandle2.getRowDescriptor().getFieldDescriptor(0), dataProvider0, gdsHelper2, false);
FBField field1 = FBField.createField(stmtHandle2.getRowDescriptor().getFieldDescriptor(1), dataProvider1, gdsHelper2, false);
FBXid xid = null;
if (dataProvider0.getRowCount() > 0) {
dataProvider0.setRow(0);
dataProvider1.setRow(0);
long inLimboTxId = field0.getLong();
byte[] inLimboMessage = field1.getBytes();
try {
xid = new FBXid(new ByteArrayInputStream(inLimboMessage), inLimboTxId);
} catch (FBIncorrectXidException ex) {
log.warn("ignoring XID stored with invalid format in RDB$TRANSACTIONS for RDB$TRANSACTION_ID=" + inLimboTxId);
}
}
stmtHandle2.close();
trHandle2.commit();
return xid;
} catch (SQLException | IOException e) {
throw new FBXAException("can't perform query to fetch xids", XAException.XAER_RMFAIL, e);
}
}
Aggregations