Search in sources :

Example 1 with GDSHelper

use of org.firebirdsql.gds.impl.GDSHelper in project jaybird by FirebirdSQL.

the class TestBackupManager method testRestorePageSize32768.

/**
 * Test if restoring a database to page size 32768 works.
 */
@Test
public void testRestorePageSize32768() throws Exception {
    assumeTrue("Test requires 32K page size support", getDefaultSupportInfo().supportsPageSize(PageSizeConstants.SIZE_32K));
    usesDatabase.createDefaultDatabase();
    backupManager.backupDatabase();
    backupManager.setRestoreReplace(true);
    backupManager.setRestorePageSize(PageSizeConstants.SIZE_32K);
    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", 32768, 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 2 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);
        final RowValue parameters = stmtHandle2.getParameterDescriptor().createDefaultFieldValues();
        FBXid tempXid = new FBXid(externalXid);
        parameters.getFieldValue(0).setFieldData(tempXid.toBytes());
        stmtHandle2.execute(parameters);
        stmtHandle2.fetchRows(1);
        FBField field0 = FBField.createField(stmtHandle2.getFieldDescriptor().getFieldDescriptor(0), dataProvider0, gdsHelper2, false);
        FBField field1 = FBField.createField(stmtHandle2.getFieldDescriptor().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 | 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) GDSHelper(org.firebirdsql.gds.impl.GDSHelper) FieldDataProvider(org.firebirdsql.jdbc.field.FieldDataProvider) ByteArrayInputStream(java.io.ByteArrayInputStream) RowValue(org.firebirdsql.gds.ng.fields.RowValue) ResourceException(javax.resource.ResourceException)

Example 3 with GDSHelper

use of org.firebirdsql.gds.impl.GDSHelper in project jaybird by FirebirdSQL.

the class FBManagedConnection method forget.

/**
 * Indicates that no further action will be taken on behalf of this
 * transaction (after a heuristic failure). It is assumed this will be
 * called after a failed commit or rollback.
 *
 * @throws XAException
 *             Occurs when the state was not correct (end never called), or
 *             the transaction ID is wrong.
 */
public void forget(Xid id) throws XAException {
    long inLimboId = -1;
    try {
        // find XID
        // TODO: Is there a reason why this piece of code can't use the JDBC Statement class?
        FbTransaction trHandle2 = database.startTransaction(tpb.getTransactionParameterBuffer());
        FbStatement stmtHandle2 = database.createStatement(trHandle2);
        GDSHelper gdsHelper2 = new GDSHelper(database);
        gdsHelper2.setCurrentTransaction(trHandle2);
        stmtHandle2.prepare(FORGET_FIND_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);
                boolean gtridEquals = Arrays.equals(xid.getGlobalTransactionId(), id.getGlobalTransactionId());
                boolean bqualEquals = Arrays.equals(xid.getBranchQualifier(), id.getBranchQualifier());
                if (gtridEquals && bqualEquals) {
                    inLimboId = inLimboTxId;
                    break;
                }
            } catch (FBIncorrectXidException ex) {
                log.warn("incorrect XID format in RDB$TRANSACTIONS where RDB$TRANSACTION_ID=" + inLimboTxId, ex);
            }
            row++;
        }
        stmtHandle2.close();
        trHandle2.commit();
    } catch (SQLException | ResourceException ex) {
        log.debug("can't perform query to fetch xids", ex);
        throw new FBXAException(XAException.XAER_RMFAIL, ex);
    }
    if (inLimboId == -1)
        // TODO: is XAER_NOTA the proper error code ?
        throw new FBXAException("XID not found", XAException.XAER_NOTA);
    try {
        // delete XID
        FbTransaction trHandle2 = database.startTransaction(tpb.getTransactionParameterBuffer());
        FbStatement stmtHandle2 = database.createStatement(trHandle2);
        GDSHelper gdsHelper2 = new GDSHelper(database);
        gdsHelper2.setCurrentTransaction(trHandle2);
        stmtHandle2.prepare(FORGET_DELETE_QUERY + inLimboId);
        stmtHandle2.execute(RowValue.EMPTY_ROW_VALUE);
        stmtHandle2.close();
        trHandle2.commit();
    } catch (SQLException ex) {
        throw new FBXAException("can't perform query to fetch xids", XAException.XAER_RMFAIL, ex);
    }
}
Also used : FBField(org.firebirdsql.jdbc.field.FBField) SQLException(java.sql.SQLException) GDSHelper(org.firebirdsql.gds.impl.GDSHelper) FieldDataProvider(org.firebirdsql.jdbc.field.FieldDataProvider) ByteArrayInputStream(java.io.ByteArrayInputStream) ResourceException(javax.resource.ResourceException)

Example 4 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 5 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)

Aggregations

GDSHelper (org.firebirdsql.gds.impl.GDSHelper)7 SQLException (java.sql.SQLException)4 ResourceException (javax.resource.ResourceException)4 FBField (org.firebirdsql.jdbc.field.FBField)4 FieldDataProvider (org.firebirdsql.jdbc.field.FieldDataProvider)4 ByteArrayInputStream (java.io.ByteArrayInputStream)3 FbDatabase (org.firebirdsql.gds.ng.FbDatabase)3 Test (org.junit.Test)3 Connection (java.sql.Connection)2 RowValue (org.firebirdsql.gds.ng.fields.RowValue)2 FBConnection (org.firebirdsql.jdbc.FBConnection)2 ArrayList (java.util.ArrayList)1 CopyOnWriteArrayList (java.util.concurrent.CopyOnWriteArrayList)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