4354 make upgrade code succeed when object_id column already exists

This commit is contained in:
William Schaefer 2018-11-08 15:29:19 -05:00
parent 3f637e12bf
commit 0ebb679778
3 changed files with 126 additions and 53 deletions

View File

@ -824,14 +824,14 @@ abstract class AbstractSqlEamDb implements EamDb {
preparedStatement.setString(4, eamArtifact.getCorrelationValue()); preparedStatement.setString(4, eamArtifact.getCorrelationValue());
preparedStatement.setString(5, eamArtifact.getFilePath().toLowerCase()); preparedStatement.setString(5, eamArtifact.getFilePath().toLowerCase());
preparedStatement.setByte(6, eamArtifact.getKnownStatus().getFileKnownValue()); preparedStatement.setByte(6, eamArtifact.getKnownStatus().getFileKnownValue());
if ("".equals(eamArtifact.getComment())) { if ("".equals(eamArtifact.getComment())) {
preparedStatement.setNull(7, Types.INTEGER); preparedStatement.setNull(7, Types.INTEGER);
} else { } else {
preparedStatement.setString(7, eamArtifact.getComment()); preparedStatement.setString(7, eamArtifact.getComment());
} }
preparedStatement.setLong(8, eamArtifact.getFileObjectId()); preparedStatement.setLong(8, eamArtifact.getFileObjectId());
preparedStatement.executeUpdate(); preparedStatement.executeUpdate();
} }
@ -1453,7 +1453,8 @@ abstract class AbstractSqlEamDb implements EamDb {
* @param type The type of instance. * @param type The type of instance.
* @param correlationCase The case tied to the instance. * @param correlationCase The case tied to the instance.
* @param correlationDataSource The data source tied to the instance. * @param correlationDataSource The data source tied to the instance.
* @param objectID The object id of the file tied to the instance. * @param objectID The object id of the file tied to the
* instance.
* *
* @return The correlation attribute if it exists; otherwise null. * @return The correlation attribute if it exists; otherwise null.
* *
@ -3139,6 +3140,18 @@ abstract class AbstractSqlEamDb implements EamDb {
); );
} }
/**
* Determine if a specific column already exists in a specific table
*
* @param tableName the table to check for the specified column
* @param columnName the name of the column to check for
*
* @return true if the column exists, false if the column does not exist
*
* @throws EamDbException
*/
abstract boolean doesColumnExist(Connection conn, String tableName, String columnName) throws SQLException;
/** /**
* Upgrade the schema of the database (if needed) * Upgrade the schema of the database (if needed)
* *
@ -3239,7 +3252,9 @@ abstract class AbstractSqlEamDb implements EamDb {
//add object_id column to existing _instances tables //add object_id column to existing _instances tables
for (CorrelationAttributeInstance.Type type : CorrelationAttributeInstance.getDefaultCorrelationTypes()) { for (CorrelationAttributeInstance.Type type : CorrelationAttributeInstance.getDefaultCorrelationTypes()) {
instance_type_dbname = EamDbUtil.correlationTypeToInstanceTableName(type); instance_type_dbname = EamDbUtil.correlationTypeToInstanceTableName(type);
statement.execute(String.format(addObjectIdColumnTemplate, instance_type_dbname)); //NON-NLS if (!doesColumnExist(conn, instance_type_dbname, "object_id")) {
statement.execute(String.format(addObjectIdColumnTemplate, instance_type_dbname)); //NON-NLS
}
statement.execute(String.format(addObjectIdIndexTemplate, instance_type_dbname, instance_type_dbname)); statement.execute(String.format(addObjectIdIndexTemplate, instance_type_dbname, instance_type_dbname));
} }
//update central repository to be able to store new correlation attributes //update central repository to be able to store new correlation attributes

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.centralrepository.datamodel; package org.sleuthkit.autopsy.centralrepository.datamodel;
import java.sql.Connection; import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -29,8 +30,7 @@ import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
/** /**
* Central Repository database implementation using Postgres as a * Central Repository database implementation using Postgres as a backend
* backend
*/ */
final class PostgresEamDb extends AbstractSqlEamDb { final class PostgresEamDb extends AbstractSqlEamDb {
@ -47,10 +47,11 @@ final class PostgresEamDb extends AbstractSqlEamDb {
/** /**
* Get the singleton instance of PostgresEamDb * Get the singleton instance of PostgresEamDb
* *
* @return the singleton instance of PostgresEamDb * @return the singleton instance of PostgresEamDb
* *
* @throws EamDbException if one or more default correlation type(s) have an invalid db table name. * @throws EamDbException if one or more default correlation type(s) have an
* invalid db table name.
*/ */
public synchronized static PostgresEamDb getInstance() throws EamDbException { public synchronized static PostgresEamDb getInstance() throws EamDbException {
if (instance == null) { if (instance == null) {
@ -61,9 +62,10 @@ final class PostgresEamDb extends AbstractSqlEamDb {
} }
/** /**
* *
* @throws EamDbException if the AbstractSqlEamDb class has one or more default * @throws EamDbException if the AbstractSqlEamDb class has one or more
* correlation type(s) having an invalid db table name. * default correlation type(s) having an invalid db
* table name.
*/ */
private PostgresEamDb() throws EamDbException { private PostgresEamDb() throws EamDbException {
dbSettings = new PostgresEamDbSettings(); dbSettings = new PostgresEamDbSettings();
@ -73,8 +75,8 @@ final class PostgresEamDb extends AbstractSqlEamDb {
@Override @Override
public void shutdownConnections() throws EamDbException { public void shutdownConnections() throws EamDbException {
try { try {
synchronized(this) { synchronized (this) {
if(connectionPool != null){ if (connectionPool != null) {
connectionPool.close(); connectionPool.close();
connectionPool = null; // force it to be re-created on next connect() connectionPool = null; // force it to be re-created on next connect()
} }
@ -148,7 +150,7 @@ final class PostgresEamDb extends AbstractSqlEamDb {
connectionURL.append(dbSettings.getPort()); connectionURL.append(dbSettings.getPort());
connectionURL.append("/"); connectionURL.append("/");
connectionURL.append(dbSettings.getDbName()); connectionURL.append(dbSettings.getDbName());
connectionPool.setUrl(connectionURL.toString()); connectionPool.setUrl(connectionURL.toString());
connectionPool.setUsername(dbSettings.getUserName()); connectionPool.setUsername(dbSettings.getUserName());
connectionPool.setPassword(dbSettings.getPassword()); connectionPool.setPassword(dbSettings.getPassword());
@ -189,31 +191,34 @@ final class PostgresEamDb extends AbstractSqlEamDb {
protected String getConflictClause() { protected String getConflictClause() {
return CONFLICT_CLAUSE; return CONFLICT_CLAUSE;
} }
/** /**
* Gets an exclusive lock (if applicable). * Gets an exclusive lock (if applicable). Will return the lock if
* Will return the lock if successful, null if unsuccessful because locking * successful, null if unsuccessful because locking isn't supported, and
* isn't supported, and throw an exception if we should have been able to get the * throw an exception if we should have been able to get the lock but failed
* lock but failed (meaning the database is in use). * (meaning the database is in use).
*
* @return the lock, or null if locking is not supported * @return the lock, or null if locking is not supported
* @throws EamDbException if the coordination service is running but we fail to get the lock *
* @throws EamDbException if the coordination service is running but we fail
* to get the lock
*/ */
@Override @Override
public CoordinationService.Lock getExclusiveMultiUserDbLock() throws EamDbException{ public CoordinationService.Lock getExclusiveMultiUserDbLock() throws EamDbException {
try { try {
// First check if multi user mode is enabled - if not there's no point trying to get a lock // First check if multi user mode is enabled - if not there's no point trying to get a lock
if( ! UserPreferences.getIsMultiUserModeEnabled()){ if (!UserPreferences.getIsMultiUserModeEnabled()) {
return null; return null;
} }
String databaseNodeName = dbSettings.getHost() + "_" + dbSettings.getDbName(); String databaseNodeName = dbSettings.getHost() + "_" + dbSettings.getDbName();
CoordinationService.Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CoordinationService.CategoryNode.CENTRAL_REPO, databaseNodeName, 5, TimeUnit.MINUTES); CoordinationService.Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CoordinationService.CategoryNode.CENTRAL_REPO, databaseNodeName, 5, TimeUnit.MINUTES);
if(lock != null){ if (lock != null) {
return lock; return lock;
} }
throw new EamDbException("Error acquiring database lock"); throw new EamDbException("Error acquiring database lock");
} catch (InterruptedException ex){ } catch (InterruptedException ex) {
throw new EamDbException("Error acquiring database lock"); throw new EamDbException("Error acquiring database lock");
} catch (CoordinationService.CoordinationServiceException ex) { } catch (CoordinationService.CoordinationServiceException ex) {
// This likely just means the coordination service isn't running, which is ok // This likely just means the coordination service isn't running, which is ok
@ -221,4 +226,22 @@ final class PostgresEamDb extends AbstractSqlEamDb {
} }
} }
@Override
boolean doesColumnExist(Connection conn, String tableName, String columnName) throws SQLException {
final String objectIdColumnExistsTemplate = "SELECT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='%s' AND column_name='%s')";
ResultSet resultSet;
Statement statement = conn.createStatement();
boolean columnExists = false;
resultSet = statement.executeQuery(String.format(objectIdColumnExistsTemplate, tableName, columnName));
try {
if (resultSet.next()) {
columnExists = resultSet.getBoolean(1);
}
} finally {
resultSet.close();
statement.close();
}
return columnExists;
}
} }

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.centralrepository.datamodel; package org.sleuthkit.autopsy.centralrepository.datamodel;
import java.sql.Connection; import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.Arrays; import java.util.Arrays;
@ -57,7 +58,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* @return the singleton instance of SqliteEamDb * @return the singleton instance of SqliteEamDb
* *
* @throws EamDbException if one or more default correlation type(s) have an * @throws EamDbException if one or more default correlation type(s) have an
* invalid db table name. * invalid db table name.
*/ */
public synchronized static SqliteEamDb getInstance() throws EamDbException { public synchronized static SqliteEamDb getInstance() throws EamDbException {
if (instance == null) { if (instance == null) {
@ -70,7 +71,8 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* *
* @throws EamDbException if the AbstractSqlEamDb class has one or more * @throws EamDbException if the AbstractSqlEamDb class has one or more
* default correlation type(s) having an invalid db table name. * default correlation type(s) having an invalid db
* table name.
*/ */
private SqliteEamDb() throws EamDbException { private SqliteEamDb() throws EamDbException {
dbSettings = new SqliteEamDbSettings(); dbSettings = new SqliteEamDbSettings();
@ -205,7 +207,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* Add a new name/value pair in the db_info table. * Add a new name/value pair in the db_info table.
* *
* @param name Key to set * @param name Key to set
* @param value Value to set * @param value Value to set
* *
* @throws EamDbException * @throws EamDbException
@ -242,7 +244,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* Update the value for a name in the name/value db_info table. * Update the value for a name in the name/value db_info table.
* *
* @param name Name to find * @param name Name to find
* @param value Value to assign to name. * @param value Value to assign to name.
* *
* @throws EamDbException * @throws EamDbException
@ -372,8 +374,8 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* Retrieves Data Source details based on data source device ID * Retrieves Data Source details based on data source device ID
* *
* @param correlationCase the current CorrelationCase used for ensuring * @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource * uniqueness of DataSource
* @param dataSourceDeviceId the data source device ID number * @param dataSourceDeviceId the data source device ID number
* *
* @return The data source * @return The data source
@ -387,13 +389,13 @@ final class SqliteEamDb extends AbstractSqlEamDb {
releaseSharedLock(); releaseSharedLock();
} }
} }
/** /**
* Retrieves Data Source details based on data source ID * Retrieves Data Source details based on data source ID
* *
* @param correlationCase the current CorrelationCase used for ensuring * @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource * uniqueness of DataSource
* @param dataSourceId the data source ID number * @param dataSourceId the data source ID number
* *
* @return The data source * @return The data source
*/ */
@ -461,7 +463,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* Retrieves eamArtifact instances from the database that are associated * Retrieves eamArtifact instances from the database that are associated
* with the aType and filePath * with the aType and filePath
* *
* @param aType EamArtifact.Type to search for * @param aType EamArtifact.Type to search for
* @param filePath File path to search for * @param filePath File path to search for
* *
* @return List of 0 or more EamArtifactInstances * @return List of 0 or more EamArtifactInstances
@ -486,7 +488,8 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* @param value The value to search for * @param value The value to search for
* *
* @return Number of artifact instances having ArtifactType and * @return Number of artifact instances having ArtifactType and
* ArtifactValue. * ArtifactValue.
*
* @throws EamDbException * @throws EamDbException
*/ */
@Override @Override
@ -518,6 +521,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* @param value The value to search for * @param value The value to search for
* *
* @return Number of unique tuples * @return Number of unique tuples
*
* @throws EamDbException * @throws EamDbException
*/ */
@Override @Override
@ -545,11 +549,11 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* associated with the caseDisplayName and dataSource of the given * associated with the caseDisplayName and dataSource of the given
* eamArtifact instance. * eamArtifact instance.
* *
* @param caseUUID Case ID to search for * @param caseUUID Case ID to search for
* @param dataSourceID Data source ID to search for * @param dataSourceID Data source ID to search for
* *
* @return Number of artifact instances having caseDisplayName and * @return Number of artifact instances having caseDisplayName and
* dataSource * dataSource
*/ */
@Override @Override
public Long getCountArtifactInstancesByCaseDataSource(String caseUUID, String dataSourceID) throws EamDbException { public Long getCountArtifactInstancesByCaseDataSource(String caseUUID, String dataSourceID) throws EamDbException {
@ -563,7 +567,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* Executes a bulk insert of the eamArtifacts added from the * Executes a bulk insert of the eamArtifacts added from the
addAttributeInstanceBulk() method * addAttributeInstanceBulk() method
*/ */
@Override @Override
public void commitAttributeInstancesBulk() throws EamDbException { public void commitAttributeInstancesBulk() throws EamDbException {
@ -596,7 +600,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* *
* @param eamArtifact Artifact containing exactly one (1) ArtifactInstance. * @param eamArtifact Artifact containing exactly one (1) ArtifactInstance.
* @param knownStatus The status to change the artifact to. Should never be * @param knownStatus The status to change the artifact to. Should never be
* KNOWN * KNOWN
*/ */
@Override @Override
public void setAttributeInstanceKnownStatus(CorrelationAttributeInstance eamArtifact, TskData.FileKnown knownStatus) throws EamDbException { public void setAttributeInstanceKnownStatus(CorrelationAttributeInstance eamArtifact, TskData.FileKnown knownStatus) throws EamDbException {
@ -633,7 +637,9 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* "Bad". * "Bad".
* *
* @param aType EamArtifact.Type to search for * @param aType EamArtifact.Type to search for
*
* @return List with 0 or more matching eamArtifact instances. * @return List with 0 or more matching eamArtifact instances.
*
* @throws EamDbException * @throws EamDbException
*/ */
@Override @Override
@ -672,7 +678,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* @param value Value to search for * @param value Value to search for
* *
* @return List of cases containing this artifact with instances marked as * @return List of cases containing this artifact with instances marked as
* bad * bad
* *
* @throws EamDbException * @throws EamDbException
*/ */
@ -690,6 +696,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* Remove a reference set and all values contained in it. * Remove a reference set and all values contained in it.
* *
* @param referenceSetID * @param referenceSetID
*
* @throws EamDbException * @throws EamDbException
*/ */
@Override @Override
@ -708,6 +715,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* @param value * @param value
* @param referenceSetID * @param referenceSetID
* @param correlationTypeID * @param correlationTypeID
*
* @return true if the hash is found in the reference set * @return true if the hash is found in the reference set
*/ */
@Override @Override
@ -723,8 +731,9 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* Process the Artifact instance in the EamDb * Process the Artifact instance in the EamDb
* *
* @param type EamArtifact.Type to search for * @param type EamArtifact.Type to search for
* @param instanceTableCallback callback to process the instance * @param instanceTableCallback callback to process the instance
*
* @throws EamDbException * @throws EamDbException
*/ */
@Override @Override
@ -736,12 +745,13 @@ final class SqliteEamDb extends AbstractSqlEamDb {
releaseSharedLock(); releaseSharedLock();
} }
} }
/** /**
* Process the Artifact instance in the EamDb * Process the Artifact instance in the EamDb
* *
* @param type EamArtifact.Type to search for * @param type EamArtifact.Type to search for
* @param instanceTableCallback callback to process the instance * @param instanceTableCallback callback to process the instance
*
* @throws EamDbException * @throws EamDbException
*/ */
@Override @Override
@ -752,7 +762,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
} finally { } finally {
releaseSharedLock(); releaseSharedLock();
} }
} }
/** /**
* Check whether a reference set with the given name/version is in the * Check whether a reference set with the given name/version is in the
@ -761,7 +771,9 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* *
* @param referenceSetName * @param referenceSetName
* @param version * @param version
*
* @return true if a matching set is found * @return true if a matching set is found
*
* @throws EamDbException * @throws EamDbException
*/ */
@Override @Override
@ -928,7 +940,8 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* Add a new reference instance * Add a new reference instance
* *
* @param eamGlobalFileInstance The reference instance to add * @param eamGlobalFileInstance The reference instance to add
* @param correlationType Correlation Type that this Reference Instance is * @param correlationType Correlation Type that this Reference
* Instance is
* *
* @throws EamDbException * @throws EamDbException
*/ */
@ -960,7 +973,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* Get all reference entries having a given correlation type and value * Get all reference entries having a given correlation type and value
* *
* @param aType Type to use for matching * @param aType Type to use for matching
* @param aValue Value to use for matching * @param aValue Value to use for matching
* *
* @return List of all global file instances with a type and value * @return List of all global file instances with a type and value
@ -1001,7 +1014,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* artifacts. * artifacts.
* *
* @return List of EamArtifact.Type's. If none are defined in the database, * @return List of EamArtifact.Type's. If none are defined in the database,
* the default list will be returned. * the default list will be returned.
* *
* @throws EamDbException * @throws EamDbException
*/ */
@ -1020,7 +1033,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* artifacts. * artifacts.
* *
* @return List of enabled EamArtifact.Type's. If none are defined in the * @return List of enabled EamArtifact.Type's. If none are defined in the
* database, the default list will be returned. * database, the default list will be returned.
* *
* @throws EamDbException * @throws EamDbException
*/ */
@ -1039,7 +1052,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* correlate artifacts. * correlate artifacts.
* *
* @return List of supported EamArtifact.Type's. If none are defined in the * @return List of supported EamArtifact.Type's. If none are defined in the
* database, the default list will be returned. * database, the default list will be returned.
* *
* @throws EamDbException * @throws EamDbException
*/ */
@ -1111,8 +1124,9 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* (meaning the database is in use). * (meaning the database is in use).
* *
* @return the lock, or null if locking is not supported * @return the lock, or null if locking is not supported
*
* @throws EamDbException if the coordination service is running but we fail * @throws EamDbException if the coordination service is running but we fail
* to get the lock * to get the lock
*/ */
@Override @Override
public CoordinationService.Lock getExclusiveMultiUserDbLock() throws EamDbException { public CoordinationService.Lock getExclusiveMultiUserDbLock() throws EamDbException {
@ -1156,4 +1170,25 @@ final class SqliteEamDb extends AbstractSqlEamDb {
rwLock.readLock().unlock(); rwLock.readLock().unlock();
} }
@Override
boolean doesColumnExist(Connection conn, String tableName, String columnName) throws SQLException {
final String tableInfoQueryTemplate = "PRAGMA table_info(%s)";
ResultSet resultSet;
Statement statement = conn.createStatement();
boolean columnExists = false;
resultSet = statement.executeQuery(String.format(tableInfoQueryTemplate, tableName));
try {
while (resultSet.next()) {
// the second value ( 2 ) is the column name
if (resultSet.getString(2).equals(columnName)) {
columnExists = true;
break;
}
}
} finally {
resultSet.close();
statement.close();
}
return columnExists;
}
} }