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)