org.firebirdsql.management.FBMaintenanceManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jaybird Show documentation
Show all versions of jaybird Show documentation
JDBC Driver for the Firebird RDBMS
/*
* Public Firebird Java API.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.firebirdsql.management;
import org.firebirdsql.gds.ServiceRequestBuffer;
import org.firebirdsql.gds.impl.GDSType;
import org.firebirdsql.gds.ng.FbExceptionBuilder;
import org.firebirdsql.gds.ng.FbService;
import org.firebirdsql.util.NumericHelper;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import static org.firebirdsql.gds.ISCConstants.*;
import static org.firebirdsql.gds.VaxEncoding.iscVaxInteger2;
import static org.firebirdsql.gds.VaxEncoding.iscVaxLong;
/**
* The {@code FBMaintenanceManager} class is responsible for replicating the functionality provided by
* the {@code gfix} command-line tool.
*
* Among the responsibilities of this class are:
*
* - Database shutdown
* - Extended database shutdown/online modes new with Firebird 2.5
* - Changing database mode to read-only or read-write
* - Enabling or disabling forced writes in the database
* - Changing the dialect of the database
* - Setting the cache size at database-level
* - Mending databases and making minor repairs
* - Sweeping databases
* - Activating and killing shadow files
* - Displaying, committing, or recovering limbo transactions
*
*
*
* @author Gabriel Reid
* @author Thomas Steinmaurer
* @author Mark Rotteveel
*/
public class FBMaintenanceManager extends FBServiceManager implements MaintenanceManager {
/**
* Create a new instance of {@code FBMaintenanceManager} based on the default GDSType.
*/
public FBMaintenanceManager() {
super();
}
/**
* Create a new instance of {@code FBMaintenanceManager} based on a given GDSType.
*
* @param gdsType
* type must be PURE_JAVA, EMBEDDED, or NATIVE
*/
public FBMaintenanceManager(String gdsType) {
super(gdsType);
}
/**
* Create a new instance of {@code FBMaintenanceManager} based on a given GDSType.
*
* @param gdsType
* The GDS implementation type to use
*/
public FBMaintenanceManager(GDSType gdsType) {
super(gdsType);
}
public void setDatabaseAccessMode(int mode) throws SQLException {
if (mode != ACCESS_MODE_READ_WRITE && mode != ACCESS_MODE_READ_ONLY) {
throw new IllegalArgumentException("mode must be one of ACCESS_MODE_READ_WRITE or ACCESS_MODE_READ_ONLY");
}
try (FbService service = attachServiceManager()) {
ServiceRequestBuffer srb = createDefaultPropertiesSRB(service);
srb.addArgument(isc_spb_prp_access_mode, (byte) mode);
executeServicesOperation(service, srb);
}
}
public void setDatabaseDialect(int dialect) throws SQLException {
if (dialect != 1 && dialect != 3) {
throw new IllegalArgumentException("dialect must be either 1 or 3");
}
try (FbService service = attachServiceManager()) {
ServiceRequestBuffer srb = createDefaultPropertiesSRB(service);
srb.addArgument(isc_spb_prp_set_sql_dialect, dialect);
executeServicesOperation(service, srb);
}
}
public void setDefaultCacheBuffer(int pageCount) throws SQLException {
if (pageCount != 0 && pageCount < 50) {
throw new IllegalArgumentException("page count must be 0 or >= 50, value was: " + pageCount);
}
try (FbService service = attachServiceManager()) {
ServiceRequestBuffer srb = createDefaultPropertiesSRB(service);
srb.addArgument(isc_spb_prp_page_buffers, pageCount);
executeServicesOperation(service, srb);
}
}
public void setForcedWrites(boolean forced) throws SQLException {
try (FbService service = attachServiceManager()) {
ServiceRequestBuffer srb = createDefaultPropertiesSRB(service);
srb.addArgument(isc_spb_prp_write_mode, (byte) (forced ? isc_spb_prp_wm_sync : isc_spb_prp_wm_async));
executeServicesOperation(service, srb);
}
}
public void setPageFill(int pageFill) throws SQLException {
if (pageFill != PAGE_FILL_FULL && pageFill != PAGE_FILL_RESERVE) {
throw new IllegalArgumentException("Page fill must be either PAGE_FILL_FULL or PAGE_FILL_RESERVE");
}
try (FbService service = attachServiceManager()) {
ServiceRequestBuffer srb = createDefaultPropertiesSRB(service);
srb.addArgument(isc_spb_prp_reserve_space, (byte) pageFill);
executeServicesOperation(service, srb);
}
}
// ----------- Database Shutdown -------------------
public void shutdownDatabase(int shutdownMode, int timeout) throws SQLException {
if (shutdownMode != SHUTDOWN_ATTACH
&& shutdownMode != SHUTDOWN_TRANSACTIONAL
&& shutdownMode != SHUTDOWN_FORCE) {
throw new IllegalArgumentException("Shutdown mode must be "
+ "one of: SHUTDOWN_ATTACH, SHUTDOWN_TRANSACTIONAL, SHUTDOWN_FORCE");
}
if (timeout < 0) {
throw new IllegalArgumentException("Timeout must be >= 0");
}
try (FbService service = attachServiceManager()) {
ServiceRequestBuffer srb = createDefaultPropertiesSRB(service);
srb.addArgument(shutdownMode, timeout);
executeServicesOperation(service, srb);
}
}
public void shutdownDatabase(byte operationMode, int shutdownModeEx, int timeout) throws SQLException {
if (operationMode != OPERATION_MODE_MULTI
&& operationMode != OPERATION_MODE_SINGLE
&& operationMode != OPERATION_MODE_FULL_SHUTDOWN) {
throw new IllegalArgumentException("Operation mode must be one of: OPERATION_MODE_MULTI, "
+ "OPERATION_MODE_SINGLE, OPERATION_MODE_FULL_SHUTDOWN");
}
if (shutdownModeEx != SHUTDOWNEX_FORCE
&& shutdownModeEx != SHUTDOWNEX_ATTACHMENTS
&& shutdownModeEx != SHUTDOWNEX_TRANSACTIONS) {
throw new IllegalArgumentException("Extended shutdown mode must be "
+ "one of: SHUTDOWNEX_FORCE, SHUTDOWNEX_ATTACHMENTS, SHUTDOWNEX_TRANSACTIONS");
}
if (timeout < 0) {
throw new IllegalArgumentException("Timeout must be >= 0");
}
try (FbService service = attachServiceManager()) {
ServiceRequestBuffer srb = createDefaultPropertiesSRB(service);
srb.addArgument(isc_spb_prp_shutdown_mode, operationMode);
srb.addArgument(shutdownModeEx, timeout);
executeServicesOperation(service, srb);
}
}
public void bringDatabaseOnline() throws SQLException {
executePropertiesOperation(isc_spb_prp_db_online);
}
public void bringDatabaseOnline(byte operationMode) throws SQLException {
if (operationMode != OPERATION_MODE_NORMAL
&& operationMode != OPERATION_MODE_MULTI
&& operationMode != OPERATION_MODE_SINGLE) {
throw new IllegalArgumentException("Operation mode must be "
+ "one of: OPERATION_MODE_NORMAL, OPERATION_MODE_MULTI, OPERATION_MODE_SINGLE");
}
try (FbService service = attachServiceManager()) {
ServiceRequestBuffer srb = createDefaultPropertiesSRB(service);
srb.addArgument(isc_spb_prp_online_mode, operationMode);
executeServicesOperation(service, srb);
}
}
// -------------- Database Repair ----------------------
public void markCorruptRecords() throws SQLException {
executeRepairOperation(isc_spb_rpr_mend_db);
}
public void validateDatabase() throws SQLException {
executeRepairOperation(isc_spb_rpr_validate_db);
}
public void validateDatabase(int options) throws SQLException {
if (options < 0
|| options != 0 && options != VALIDATE_IGNORE_CHECKSUM
&& (options & ~VALIDATE_IGNORE_CHECKSUM) != VALIDATE_READ_ONLY
&& (options & ~VALIDATE_IGNORE_CHECKSUM) != VALIDATE_FULL
&& (options | (VALIDATE_READ_ONLY | VALIDATE_IGNORE_CHECKSUM)) != options
&& (options | (VALIDATE_FULL | VALIDATE_IGNORE_CHECKSUM)) != options) {
throw new IllegalArgumentException("options must be either 0, "
+ "VALIDATE_READ_ONLY, or VALIDATE_FULL, optionally combined with VALIDATE_IGNORE_CHECKSUM");
}
try (FbService service = attachServiceManager()) {
ServiceRequestBuffer srb = createRepairSRB(service, options | isc_spb_rpr_validate_db);
executeServicesOperation(service, srb);
}
}
// ----------- Sweeping -------------------------
public void setSweepThreshold(int transactions) throws SQLException {
if (transactions < 0) {
throw new IllegalArgumentException("transactions must be >= 0");
}
try (FbService service = attachServiceManager()) {
ServiceRequestBuffer srb = createDefaultPropertiesSRB(service);
srb.addArgument(isc_spb_prp_sweep_interval, transactions);
executeServicesOperation(service, srb);
}
}
public void sweepDatabase() throws SQLException {
executeRepairOperation(isc_spb_rpr_sweep_db);
}
// ----------- Shadow Files ------------------------------------
public void activateShadowFile() throws SQLException {
executePropertiesOperation(isc_spb_prp_activate);
}
public void killUnavailableShadows() throws SQLException {
executeRepairOperation(isc_spb_rpr_kill_shadows);
}
// ----------- Transaction Management ----------------------------
public List limboTransactionsAsList() throws SQLException {
// See also fbscvmgr.cpp method printInfo
final OutputStream saveOut = getLogger();
final List result = new ArrayList<>();
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final byte[] output;
try {
setLogger(out);
executeRepairOperation(isc_spb_rpr_list_limbo_trans);
output = out.toByteArray();
} finally {
setLogger(saveOut);
}
int idx = 0;
while (idx < output.length) {
switch (output[idx++]) {
case isc_spb_single_tra_id:
case isc_spb_multi_tra_id: {
long trId = iscVaxLong(output, idx, 4);
idx += 4;
result.add(trId);
break;
}
case isc_spb_single_tra_id_64:
case isc_spb_multi_tra_id_64: {
long trId = iscVaxLong(output, idx, 8);
idx += 8;
result.add(trId);
break;
}
// Information items we will ignore for now
case isc_spb_tra_id:
idx += 4;
break;
case isc_spb_tra_id_64:
idx += 8;
break;
case isc_spb_tra_state:
case isc_spb_tra_advise:
idx++;
break;
case isc_spb_tra_host_site:
case isc_spb_tra_remote_site:
case isc_spb_tra_db_path:
int length = iscVaxInteger2(output, idx);
idx += 2;
idx += length;
break;
default:
throw new FbExceptionBuilder()
.exception(isc_fbsvcmgr_info_err)
.messageParameter(output[idx - 1] & 0xFF)
.toSQLException();
}
}
return result;
}
@Override
public long[] getLimboTransactions() throws SQLException {
final List limboTransactions = limboTransactionsAsList();
final long[] trans = new long[limboTransactions.size()];
int idx = 0;
for (long trId : limboTransactions) {
trans[idx++] = trId;
}
return trans;
}
@Override
public void commitTransaction(final long transactionId) throws SQLException {
handleTransaction(transactionId, isc_spb_rpr_commit_trans, isc_spb_rpr_commit_trans_64);
}
@Override
public void rollbackTransaction(final long transactionId) throws SQLException {
handleTransaction(transactionId, isc_spb_rpr_rollback_trans, isc_spb_rpr_rollback_trans_64);
}
private void handleTransaction(final long transactionId, final int action32bit, final int action64bit)
throws SQLException {
if (transactionId < 0) {
throw new SQLException("Only positive transactionIds are supported");
}
final boolean is32Bit = NumericHelper.fitsUnsigned32BitInteger(transactionId);
try (FbService service = attachServiceManager()) {
ServiceRequestBuffer srb = createDefaultRepairSRB(service);
srb.addArgument(is32Bit ? action32bit : action64bit, transactionId);
executeServicesOperation(service, srb);
}
}
@Override
public void upgradeOds() throws SQLException {
executeRepairOperation(isc_spb_rpr_upgrade_db);
}
@Override
public void fixIcu() throws SQLException {
executeRepairOperation(isc_spb_rpr_icu);
}
// ----------- Private implementation methods --------------------
/**
* Execute an isc_spb_rpr_* (repair) services operation.
*
* @param operation
* The identifier for the operation to be executed
* @throws SQLException
* if a database access error occurs
*/
private void executeRepairOperation(int operation) throws SQLException {
try (FbService service = attachServiceManager()) {
ServiceRequestBuffer srb = createRepairSRB(service, operation);
executeServicesOperation(service, srb);
}
}
/**
* Execute a isc_spb_prp_* (properties) services operation.
*
* @param operation
* The identifier for the operation to be executed
* @throws SQLException
* if a database access error occurs
*/
private void executePropertiesOperation(int operation) throws SQLException {
try (FbService service = attachServiceManager()) {
ServiceRequestBuffer srb = createPropertiesSRB(service, operation);
executeServicesOperation(service, srb);
}
}
/**
* Get a mostly empty properties-operation buffer that can be filled in as
* needed. The buffer created by this method cannot have the options
* bitmask set on it.
*/
private ServiceRequestBuffer createDefaultPropertiesSRB(FbService service) {
return createPropertiesSRB(service, 0);
}
/**
* Get a mostly empty repair-operation buffer that can be filled in as
* needed. The buffer created by this method cannot have the options
* bitmask set on it.
*/
private ServiceRequestBuffer createDefaultRepairSRB(FbService service) {
return createRepairSRB(service, 0);
}
/**
* Get a mostly-empty properties-operation request buffer that can be
* filled as needed.
*
* @param service
* Service handle
* @param options
* The options bitmask for the request buffer
*/
private ServiceRequestBuffer createPropertiesSRB(FbService service, int options) {
return createRequestBuffer(service, isc_action_svc_properties, options);
}
/**
* Get a mostly-empty repair-operation request buffer that can be
* filled as needed.
*
* @param service
* Service handle
* @param options
* The options bitmask for the request buffer
*/
private ServiceRequestBuffer createRepairSRB(FbService service, int options) {
ServiceRequestBuffer srb = createRequestBuffer(service, isc_action_svc_repair, options);
if (getParallelWorkers() > 0 && (options & getParallelRepairOptions(service)) != 0) {
srb.addArgument(isc_spb_rpr_par_workers, getParallelWorkers());
}
return srb;
}
/**
* Bitmask of repair options which support parallel workers.
*
* @param service service attachment, to determine supported options
* @return bitmask of repair options
*/
private int getParallelRepairOptions(FbService service) {
if (service.getServerVersion().isEqualOrAbove(5, 0)) {
return isc_spb_rpr_sweep_db | isc_spb_rpr_icu;
} else {
return 0;
}
}
}