Search in sources :

Example 6 with GDSHelper

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);
    }
}
Also used : FBField(org.firebirdsql.jdbc.field.FBField) SQLException(java.sql.SQLException) CopyOnWriteArrayList(java.util.concurrent.CopyOnWriteArrayList) GDSHelper(org.firebirdsql.gds.impl.GDSHelper) FieldDataProvider(org.firebirdsql.jdbc.field.FieldDataProvider) ByteArrayInputStream(java.io.ByteArrayInputStream) ResourceException(javax.resource.ResourceException)

Example 7 with GDSHelper

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);
    }
}
Also used : SQLException(java.sql.SQLException) GDSHelper(org.firebirdsql.gds.impl.GDSHelper) ResourceException(javax.resource.ResourceException) FbStatement(org.firebirdsql.gds.ng.FbStatement) FbDatabase(org.firebirdsql.gds.ng.FbDatabase) FbTransaction(org.firebirdsql.gds.ng.FbTransaction)

Example 8 with GDSHelper

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);
    }
}
Also used : FBField(org.firebirdsql.jdbc.field.FBField) DefaultStatementListener(org.firebirdsql.gds.ng.listeners.DefaultStatementListener) StatementListener(org.firebirdsql.gds.ng.listeners.StatementListener) ArrayList(java.util.ArrayList) GDSHelper(org.firebirdsql.gds.impl.GDSHelper) FieldDataProvider(org.firebirdsql.jdbc.field.FieldDataProvider) DefaultStatementListener(org.firebirdsql.gds.ng.listeners.DefaultStatementListener) RowValue(org.firebirdsql.gds.ng.fields.RowValue) Test(org.junit.Test)

Example 9 with GDSHelper

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);
    }
}
Also used : FBConnection(org.firebirdsql.jdbc.FBConnection) Connection(java.sql.Connection) FBConnection(org.firebirdsql.jdbc.FBConnection) GDSHelper(org.firebirdsql.gds.impl.GDSHelper) FbDatabase(org.firebirdsql.gds.ng.FbDatabase) Test(org.junit.Test)

Example 10 with GDSHelper

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);
    }
}
Also used : FBField(org.firebirdsql.jdbc.field.FBField) SQLException(java.sql.SQLException) GDSHelper(org.firebirdsql.gds.impl.GDSHelper) IOException(java.io.IOException) FieldDataProvider(org.firebirdsql.jdbc.field.FieldDataProvider) ByteArrayInputStream(java.io.ByteArrayInputStream) RowValue(org.firebirdsql.gds.ng.fields.RowValue)

Aggregations

GDSHelper (org.firebirdsql.gds.impl.GDSHelper)10 SQLException (java.sql.SQLException)7 FBField (org.firebirdsql.jdbc.field.FBField)7 FieldDataProvider (org.firebirdsql.jdbc.field.FieldDataProvider)7 ByteArrayInputStream (java.io.ByteArrayInputStream)6 ResourceException (javax.resource.ResourceException)4 IOException (java.io.IOException)3 FbDatabase (org.firebirdsql.gds.ng.FbDatabase)3 RowValue (org.firebirdsql.gds.ng.fields.RowValue)3 Test (org.junit.Test)3 Connection (java.sql.Connection)2 CopyOnWriteArrayList (java.util.concurrent.CopyOnWriteArrayList)2 FBConnection (org.firebirdsql.jdbc.FBConnection)2 ArrayList (java.util.ArrayList)1 FbStatement (org.firebirdsql.gds.ng.FbStatement)1 FbTransaction (org.firebirdsql.gds.ng.FbTransaction)1 DefaultStatementListener (org.firebirdsql.gds.ng.listeners.DefaultStatementListener)1 StatementListener (org.firebirdsql.gds.ng.listeners.StatementListener)1