19 package org.sleuthkit.datamodel;
 
   21 import com.google.common.annotations.Beta;
 
   22 import java.sql.PreparedStatement;
 
   23 import java.sql.ResultSet;
 
   24 import java.sql.SQLException;
 
   25 import java.sql.Statement;
 
   27 import java.sql.Timestamp;
 
   28 import java.text.MessageFormat;
 
   30 import java.util.logging.Level;
 
   31 import java.util.logging.Logger;
 
   57            void process(ResultSet resultSet);
 
   87                 boolean doesColumnExists = 
false;
 
   90                         doesColumnExists = 
columnExists(tableName, columnName, localTrans);
 
   95                         if (null != localTrans) {
 
   99                                         logger.log(Level.SEVERE, 
"Failed to rollback transaction after exception", ex);
 
  104         return doesColumnExists;
 
  120         Statement statement = null;
 
  121                 ResultSet resultSet = null;
 
  123                         CaseDbConnection connection = transaction.getConnection();
 
  124                         statement = connection.createStatement();
 
  126                                 String tableInfoQuery = 
"PRAGMA table_info(%s)";  
 
  127                                 resultSet = statement.executeQuery(String.format(tableInfoQuery, tableName));
 
  128                                 while (resultSet.next()) {
 
  129                                         if (resultSet.getString(
"name").equalsIgnoreCase(columnName)) {
 
  136                                 String tableInfoQueryTemplate = 
"SELECT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='%s' AND column_name='%s')";  
 
  137                                 resultSet = statement.executeQuery(String.format(tableInfoQueryTemplate, tableName.toLowerCase(), columnName.toLowerCase()));
 
  138                                 if (resultSet.next()) {
 
  139                                         columnExists = resultSet.getBoolean(1);
 
  143                 catch (SQLException ex) {
 
  144                         throw new TskCoreException(
"Error checking if column  " + columnName + 
"exists ", ex);
 
  147                         if (resultSet != null) {
 
  150                                 } 
catch (SQLException ex2) {
 
  151                                         logger.log(Level.WARNING, 
"Failed to to close resultset after checking column", ex2);
 
  154             closeStatement(statement);
 
  169                 boolean doesTableExist = 
false;
 
  172                         doesTableExist = 
tableExists(tableName, localTrans);
 
  177                         if (null != localTrans) {
 
  181                                         logger.log(Level.SEVERE, 
"Failed to rollback transaction after exception", ex); 
 
  186         return doesTableExist;
 
  201         Statement statement = null;
 
  202                 ResultSet resultSet = null;
 
  204                         CaseDbConnection connection = transaction.getConnection();
 
  205                         statement = connection.createStatement();
 
  207                                 resultSet = statement.executeQuery(
"SELECT name FROM sqlite_master WHERE type='table'");  
 
  208                                 while (resultSet.next()) {
 
  209                                         if (resultSet.getString(
"name").equalsIgnoreCase(tableName)) { 
 
  216                                 String tableInfoQueryTemplate = 
"SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name='%s')";  
 
  217                                 resultSet = statement.executeQuery(String.format(tableInfoQueryTemplate, tableName.toLowerCase()));
 
  218                                 if (resultSet.next()) {
 
  219                                         tableExists = resultSet.getBoolean(1);
 
  223                 catch (SQLException ex) {
 
  224                         throw new TskCoreException(
"Error checking if table  " + tableName + 
"exists ", ex);
 
  226                         if (resultSet != null) {
 
  229                                 } 
catch (SQLException ex2) {
 
  230                                         logger.log(Level.WARNING, 
"Failed to to close resultset after checking table", ex2);
 
  233             closeStatement(statement);
 
  253                 validateTableName(tableName);
 
  254                 validateSQL(tableSchema);
 
  257                 String createSQL = 
"CREATE TABLE IF NOT EXISTS " + tableName + 
" " + tableSchema;
 
  258                 try (CaseDbConnection connection = tskDB.getConnection();
 
  259                                 Statement statement = connection.createStatement();) {
 
  260                         statement.execute(createSQL);
 
  261                 } 
catch (SQLException ex) {
 
  284                         if (null != localTrans) {
 
  288                                         logger.log(Level.SEVERE, 
"Failed to rollback transaction after exception", ex);
 
  305                 validateTableName(tableName);
 
  306                 validateSQL(alterSQL);
 
  308                 CaseDbConnection connection = transaction.getConnection();
 
  310                 Statement statement = null;
 
  311                 String sql = 
"ALTER TABLE " + tableName + 
" " + alterSQL;
 
  314                         statement = connection.createStatement();
 
  315                         statement.execute(sql);
 
  316                 } 
catch (SQLException ex) {
 
  321                                         alterSQL.toLowerCase().contains(
"add column") &&
 
  322                                         ex.getMessage().toLowerCase().contains(
"duplicate column name")) {
 
  323                                 logger.log(Level.WARNING, String.format(
"Column being added by SQL = %s already exists in table %s", alterSQL, tableName));
 
  326                         throw new TskCoreException(String.format(
"Error altering table  %s with SQL = %s", tableName, sql), ex);
 
  328                         closeStatement(statement);
 
  348                 validateTableName(tableName);
 
  349                 validateIndexName(indexName);
 
  350                 validateSQL(colsSQL);
 
  353                 String indexSQL = 
"CREATE INDEX IF NOT EXISTS " + indexName + 
" ON " + tableName + 
" " + colsSQL; 
 
  354                 try (CaseDbConnection connection = tskDB.getConnection();
 
  355                         Statement statement = connection.createStatement(); ) {
 
  356                         statement.execute(indexSQL);
 
  357                 } 
catch (SQLException ex) {
 
  378                         long rowId = 
insert(tableName, sql, localTrans);
 
  383                         if (null != localTrans) {
 
  387                                         logger.log(Level.SEVERE, 
"Failed to rollback transaction after exception", ex);
 
  413                 validateTableName(tableName);
 
  416                 CaseDbConnection connection = transaction.getConnection();
 
  418                 PreparedStatement statement = null;
 
  420                 String insertSQL = 
"INSERT";
 
  422                         insertSQL += 
" OR IGNORE";
 
  425                 insertSQL = insertSQL+ 
" INTO " + tableName + 
" " + sql; 
 
  427                         statement = connection.prepareStatement(insertSQL, Statement.RETURN_GENERATED_KEYS);
 
  428                         connection.executeUpdate(statement);
 
  430                         resultSet = statement.getGeneratedKeys();
 
  431                         if (resultSet.next()) {
 
  432                                 rowId = resultSet.getLong(1); 
 
  434                 } 
catch (SQLException ex) {
 
  435                         throw new TskCoreException(
"Error inserting row in table " + tableName + 
" with sql = "+ insertSQL, ex);
 
  437                         closeStatement(statement);
 
  466                         if (null != localTrans) {
 
  470                                         logger.log(Level.SEVERE, 
"Failed to rollback transaction after exception", ex);
 
  496                 validateTableName(tableName);
 
  499                 CaseDbConnection connection = transaction.getConnection();
 
  501                 PreparedStatement statement = null;
 
  503                 String insertSQL = 
"INSERT";
 
  505                         insertSQL += 
" OR REPLACE";
 
  508                 insertSQL += 
" INTO " + tableName + 
" " + sql; 
 
  510                         statement = connection.prepareStatement(insertSQL, Statement.RETURN_GENERATED_KEYS);
 
  511                         connection.executeUpdate(statement);
 
  513                         resultSet = statement.getGeneratedKeys();
 
  515                         rowId = resultSet.getLong(1); 
 
  516                 } 
catch (SQLException ex) {
 
  517                         throw new TskCoreException(
"Error inserting row in table " + tableName + 
" with sql = "+ insertSQL, ex);
 
  519                         closeStatement(statement);
 
  540                 validateTableName(tableName);
 
  543                 String updateSQL = 
"UPDATE " + tableName + 
" " + sql; 
 
  547                 } 
catch (SQLException ex) {
 
  548                         throw new TskCoreException(
"Error creating update prepared statement for query:\n" + updateSQL, ex);
 
  562                 if (!preparedStatement.getType().equals(StatementType.UPDATE)) {
 
  563                         throw new TskCoreException(
"CaseDbPreparedStatement has incorrect type for update operation");
 
  567                         preparedStatement.getStatement().executeUpdate();
 
  568                 } 
catch (SQLException ex) {
 
  569                         throw new TskCoreException(
"Error updating row in table " + 
"" + 
" with sql = "+ 
"", ex);
 
  584                         update(tableName, sql, localTrans);
 
  588                         if (null != localTrans) {
 
  592                                         logger.log(Level.SEVERE, 
"Failed to rollback transaction after exception", ex);
 
  611                 validateTableName(tableName);
 
  614                 CaseDbConnection connection = transaction.getConnection();
 
  616                 Statement statement = null;
 
  617                 String updateSQL = 
"UPDATE " + tableName + 
" " + sql; 
 
  620                         statement = connection.createStatement();
 
  621                         statement.executeUpdate(updateSQL);
 
  622                 } 
catch (SQLException ex) {
 
  625                         closeStatement(statement);
 
  639                 if (queryCallback == null) {
 
  646                 String selectSQL = 
"SELECT " +  sql; 
 
  647                 try (CaseDbConnection connection = tskDB.getConnection();
 
  648                         Statement statement = connection.createStatement();
 
  649                         ResultSet resultSet = statement.executeQuery(selectSQL)) {
 
  650                         queryCallback.process(resultSet);
 
  651                 } 
catch (SQLException ex) {
 
  678                 String selectSQL = 
"SELECT " + sql; 
 
  681                 } 
catch (SQLException ex) {
 
  682                         throw new TskCoreException(
"Error creating select prepared statement for query:\n" + selectSQL, ex);
 
  701                 String selectSQL = 
"SELECT " + sql; 
 
  705                 } 
catch (SQLException ex) {
 
  706                         throw new TskCoreException(
"Error creating select prepared statement for query:\n" + selectSQL, ex);
 
  721                 if (!preparedStatement.getType().equals(StatementType.SELECT)) {
 
  722                         throw new TskCoreException(
"CaseDbPreparedStatement has incorrect type for select operation");
 
  725                 try (ResultSet resultSet = preparedStatement.getStatement().executeQuery()) {
 
  726                         queryCallback.process(resultSet);
 
  727                 } 
catch (SQLException ex) {
 
  728                         throw new TskCoreException(MessageFormat.format(
"Error running SELECT query:\n{0}", preparedStatement.getOriginalSql()), ex);
 
  749                 validateTableName(tableName);
 
  752                 String insertSQL = 
"INSERT";
 
  754                         insertSQL += 
" OR IGNORE";
 
  756                 insertSQL = insertSQL + 
" INTO " + tableName + 
" " + sql; 
 
  760                 } 
catch (SQLException ex) {
 
  761                         throw new TskCoreException(
"Error creating insert prepared statement for query:\n" + insertSQL, ex);
 
  775                 if (!preparedStatement.getType().equals(StatementType.INSERT)) {
 
  776                         throw new TskCoreException(
"CaseDbPreparedStatement has incorrect type for insert operation");
 
  780                         preparedStatement.getStatement().executeUpdate();
 
  781                 } 
catch (SQLException ex) {
 
  782                         throw new TskCoreException(
"Error inserting row in table " + 
"" + 
" with sql = "+ 
"", ex);
 
  795                 validateTableName(tableName);
 
  799                 String deleteSQL = 
"DELETE FROM " + tableName + 
" " + sql; 
 
  800                 try (CaseDbConnection connection = tskDB.getConnection();
 
  801                         Statement statement = connection.createStatement();) {
 
  802                         statement.executeUpdate(deleteSQL);
 
  803                 } 
catch (SQLException ex) {
 
  804                         throw new TskCoreException(
"Error deleting row from table " + tableName, ex);
 
  820                 if (
SleuthkitCase.getCoreTableNames().contains(tableName.toLowerCase())) {
 
  821                         throw new TskCoreException(
"Attempt to modify a core TSK table " + tableName);
 
  823                 if (tableName.toLowerCase().startsWith(
"tsk_")) {
 
  824                         throw new TskCoreException(
"Modifying tables with tsk_ prefix is not allowed. ");
 
  836         private void validateIndexName(String indexName) 
throws TskCoreException {
 
  838                 if (indexName.isEmpty()) {
 
  839                         throw new TskCoreException(
"Invalid index name " + indexName);  
 
  842                 if (SleuthkitCase.getCoreIndexNames().contains(indexName.toLowerCase())) {
 
  843                         throw new TskCoreException(
"Attempt to modify a core TSK index " + indexName);  
 
  854         private void validateSQL(String sql) 
throws TskCoreException {
 
  863         private enum LockType {
 
  872         private enum StatementType {
 
  885                 private final CaseDbConnection connection;
 
  886                 private final PreparedStatement preparedStatement;
 
  887                 private final String originalSql;
 
  888                 private final LockType lockType;
 
  889                 private final StatementType type;
 
  913                         if (isWriteLockRequired) {
 
  915                                 this.lockType = LockType.WRITE;
 
  918                                 this.lockType = LockType.READ;
 
  920                         this.connection = tskDB.getConnection();
 
  921                         this.preparedStatement = connection.getPreparedStatement(query, Statement.NO_GENERATED_KEYS);
 
  922                         this.originalSql = query;
 
  937                         this.lockType = LockType.NONE;
 
  938                         this.connection = trans.getConnection();
 
  939                         this.preparedStatement = connection.getPreparedStatement(query, Statement.NO_GENERATED_KEYS);
 
  940                         this.originalSql = query;
 
  949                 private PreparedStatement getStatement() {
 
  950                         return preparedStatement;
 
  958                 private StatementType getType() {
 
  967                 private String getOriginalSql() {
 
  978                                 preparedStatement.clearParameters();
 
  979                         } 
catch (SQLException ex) {
 
  980                                 throw new TskCoreException(
"An error occurred while clearing parameters.", ex);
 
  994                                 preparedStatement.setBoolean(parameterIndex, x);
 
  995                         } 
catch (SQLException ex) {
 
  996                                 throw new TskCoreException(MessageFormat.format(
"There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
 
 1010                                 preparedStatement.setByte(parameterIndex, x);
 
 1011                         } 
catch (SQLException ex) {
 
 1012                                 throw new TskCoreException(MessageFormat.format(
"There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
 
 1026                                 preparedStatement.setInt(parameterIndex, x);
 
 1027                         } 
catch (SQLException ex) {
 
 1028                                 throw new TskCoreException(MessageFormat.format(
"There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
 
 1042                                 preparedStatement.setLong(parameterIndex, x);
 
 1043                         } 
catch (SQLException ex) {
 
 1044                                 throw new TskCoreException(MessageFormat.format(
"There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
 
 1058                                 preparedStatement.setDouble(parameterIndex, x);
 
 1059                         } 
catch (SQLException ex) {
 
 1060                                 throw new TskCoreException(MessageFormat.format(
"There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
 
 1074                                 preparedStatement.setString(parameterIndex, x);
 
 1075                         } 
catch (SQLException ex) {
 
 1076                                 throw new TskCoreException(MessageFormat.format(
"There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
 
 1090                                 preparedStatement.setDate(parameterIndex, x);
 
 1091                         } 
catch (SQLException ex) {
 
 1092                                 throw new TskCoreException(MessageFormat.format(
"There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
 
 1106                                 preparedStatement.setTime(parameterIndex, x);
 
 1107                         } 
catch (SQLException ex) {
 
 1108                                 throw new TskCoreException(MessageFormat.format(
"There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
 
 1122                                 preparedStatement.setTimestamp(parameterIndex, x);
 
 1123                         } 
catch (SQLException ex) {
 
 1124                                 throw new TskCoreException(MessageFormat.format(
"There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
 
 1140                                 preparedStatement.setObject(parameterIndex, x);
 
 1141                         } 
catch (SQLException ex) {
 
 1142                                 throw new TskCoreException(MessageFormat.format(
"There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
 
 1147                 public void close() throws SQLException {
 
 1151                         if (lockType.equals(LockType.NONE)) {
 
 1156                         if (lockType.equals(LockType.WRITE)) {
 
void process(ResultSet resultSet)
 
boolean tableExists(String tableName, CaseDbTransaction transaction)
 
long insertOrUpdate(final String tableName, final String sql)
 
CaseDbTransaction beginTransaction()
 
void setInt(int parameterIndex, int x)
 
void setTime(int parameterIndex, Time x)
 
void update(final String tableName, final String sql)
 
void setDate(int parameterIndex, Date x)
 
long insert(final String tableName, final String sql, final CaseDbTransaction transaction)
 
CaseDbPreparedStatement prepareSelect(String sql)
 
void alterTable(final String tableName, final String alterSQL, final CaseDbTransaction transaction)
 
boolean tableExists(String tableName)
 
void createIndex(final String indexName, final String tableName, final String colsSQL)
 
boolean columnExists(String tableName, String columnName)
 
void setDouble(int parameterIndex, double x)
 
void releaseSingleUserCaseReadLock()
 
void createTable(final String tableName, final String tableSchema)
 
long insertOrUpdate(final String tableName, final String sql, final CaseDbTransaction transaction)
 
void acquireSingleUserCaseWriteLock()
 
void releaseSingleUserCaseWriteLock()
 
void insert(CaseDbPreparedStatement preparedStatement)
 
CaseDbPreparedStatement prepareUpdate(String tableName, String sql, CaseDbTransaction trans)
 
void alterTable(final String tableName, final String alterSQL)
 
void update(CaseDbPreparedStatement preparedStatement)
 
void select(final String sql, final CaseDbAccessQueryCallback queryCallback)
 
CaseDbPreparedStatement prepareInsert(String tableName, String sql, CaseDbTransaction trans)
 
void update(final String tableName, final String sql, CaseDbTransaction transaction)
 
void setByte(int parameterIndex, byte x)
 
CaseDbPreparedStatement prepareSelect(String sql, CaseDbTransaction trans)
 
void acquireSingleUserCaseReadLock()
 
long insert(final String tableName, final String sql)
 
void setBoolean(int parameterIndex, boolean x)
 
void setTimestamp(int parameterIndex, Timestamp x)
 
void setObject(int parameterIndex, Object x)
 
void setString(int parameterIndex, String x)
 
void select(CaseDbPreparedStatement preparedStatement, CaseDbAccessQueryCallback queryCallback)
 
boolean columnExists(String tableName, String columnName, CaseDbTransaction transaction)
 
void setLong(int parameterIndex, long x)