use of org.firebirdsql.gds.ng.FbTransaction in project jaybird by FirebirdSQL.
the class TestV10OutputBlob method testUsingCancelledBlob.
/**
* Test whether a cancelled blob cannot be used (indicating it was indeed cancelled).
*/
@Test
public void testUsingCancelledBlob() throws Exception {
expectedException.expect(SQLException.class);
expectedException.expect(allOf(errorCodeEquals(ISCConstants.isc_bad_segstr_id), message(startsWith(getFbMessage(ISCConstants.isc_bad_segstr_id)))));
final int testId = 1;
final byte[] baseContent = generateBaseContent();
final int requiredSize = 256;
final byte[] testBytes = generateBlobContent(baseContent, requiredSize);
try (FbWireDatabase db = createDatabaseConnection()) {
final SimpleStatementListener listener = new SimpleStatementListener();
final FbTransaction transaction = getTransaction(db);
try {
final FbStatement statement = db.createStatement(transaction);
statement.addStatementListener(listener);
final FbBlob blob = db.createBlobForOutput(transaction, null);
blob.open();
int bytesWritten = 0;
while (bytesWritten < testBytes.length) {
// TODO the interface for writing blobs should be simpler
byte[] buffer = new byte[Math.min(blob.getMaximumSegmentSize(), testBytes.length - bytesWritten)];
System.arraycopy(testBytes, bytesWritten, buffer, 0, buffer.length);
blob.putSegment(buffer);
bytesWritten += buffer.length;
}
blob.cancel();
statement.prepare(INSERT_BLOB_TABLE);
final DatatypeCoder datatypeCoder = db.getDatatypeCoder();
FieldValue param1 = new FieldValue(datatypeCoder.encodeInt(testId));
FieldValue param2 = new FieldValue(datatypeCoder.encodeLong(blob.getBlobId()));
statement.execute(RowValue.of(param1, param2));
statement.close();
} finally {
transaction.commit();
}
}
}
use of org.firebirdsql.gds.ng.FbTransaction in project jaybird by FirebirdSQL.
the class TestV10OutputBlob method testIsEof_afterOpen.
/**
* Test if blob is not eof after open.
*/
@Test
public void testIsEof_afterOpen() throws Exception {
try (FbWireDatabase db = createDatabaseConnection()) {
final FbTransaction transaction = getTransaction(db);
try {
FbBlob blob = db.createBlobForOutput(transaction, null);
assumeTrue("Output blob before open should be eof", blob.isEof());
blob.open();
assertFalse("Output blob after open should not be eof", blob.isEof());
} finally {
transaction.commit();
}
}
}
use of org.firebirdsql.gds.ng.FbTransaction in project jaybird by FirebirdSQL.
the class TestV10OutputBlob method testIsEof_afterCancel.
/**
* Test if blob is eof after cancel.
*/
@Test
public void testIsEof_afterCancel() throws Exception {
try (FbWireDatabase db = createDatabaseConnection()) {
final FbTransaction transaction = getTransaction(db);
try {
FbBlob blob = db.createBlobForOutput(transaction, null);
assumeTrue("Output blob before open should be eof", blob.isEof());
blob.open();
blob.cancel();
assertTrue("Output blob after cancel should be eof", blob.isEof());
} finally {
transaction.commit();
}
}
}
use of org.firebirdsql.gds.ng.FbTransaction in project jaybird by FirebirdSQL.
the class TestJnaDatabase method testDetach_openTransactions.
@Test
public void testDetach_openTransactions() throws Exception {
FBManager fbManager = createFBManager();
defaultDatabaseSetUp(fbManager);
try {
JnaDatabase db = factory.connect(connectionInfo);
FbTransaction transaction = null;
try {
db.attach();
// Starting an active transaction
transaction = getTransaction(db);
expectedException.expect(allOf(errorCodeEquals(ISCConstants.isc_open_trans), message(startsWith(getFbMessage(ISCConstants.isc_open_trans, "1")))));
db.close();
} finally {
if (transaction != null && transaction.getState() == TransactionState.ACTIVE) {
transaction.commit();
}
safelyClose(db);
}
} finally {
defaultDatabaseTearDown(fbManager);
}
}
use of org.firebirdsql.gds.ng.FbTransaction 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);
}
}
Aggregations