indev
This commit is contained in:
@@ -0,0 +1,607 @@
|
||||
#include "DatabaseStorage.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "SourcetrailException.h"
|
||||
#include "NodeKind.h"
|
||||
#include "utility.h"
|
||||
#include "version.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace sourcetrail
|
||||
{
|
||||
// --- Public Interface ---
|
||||
|
||||
int DatabaseStorage::getSupportedDatabaseVersion()
|
||||
{
|
||||
return DATABASE_VERSION;
|
||||
}
|
||||
|
||||
std::shared_ptr<DatabaseStorage> DatabaseStorage::openDatabase(const std::string& dbFilePath)
|
||||
{
|
||||
std::shared_ptr<DatabaseStorage> storage = std::shared_ptr<DatabaseStorage>(new DatabaseStorage());
|
||||
storage->m_database.open(dbFilePath.c_str());
|
||||
storage->executeStatement("PRAGMA foreign_keys=ON;");
|
||||
return storage;
|
||||
}
|
||||
|
||||
DatabaseStorage::~DatabaseStorage()
|
||||
{
|
||||
m_database.close();
|
||||
}
|
||||
|
||||
void DatabaseStorage::setupTables()
|
||||
{
|
||||
executeStatement("PRAGMA foreign_keys=ON;");
|
||||
|
||||
if (!isCompatible())
|
||||
{
|
||||
throw SourcetrailException("Unable to setup database tables because database is not compatible.");
|
||||
}
|
||||
|
||||
executeStatement(
|
||||
"CREATE TABLE IF NOT EXISTS meta("
|
||||
" id INTEGER, "
|
||||
" key TEXT, "
|
||||
" value TEXT, "
|
||||
" PRIMARY KEY(id)"
|
||||
");"
|
||||
);
|
||||
|
||||
executeStatement(
|
||||
"CREATE TABLE IF NOT EXISTS element("
|
||||
" id INTEGER, "
|
||||
" PRIMARY KEY(id)"
|
||||
");"
|
||||
);
|
||||
|
||||
executeStatement(
|
||||
"CREATE TABLE IF NOT EXISTS edge("
|
||||
" id INTEGER NOT NULL, "
|
||||
" type INTEGER NOT NULL, "
|
||||
" source_node_id INTEGER NOT NULL, "
|
||||
" target_node_id INTEGER NOT NULL, "
|
||||
" PRIMARY KEY(id), "
|
||||
" FOREIGN KEY(id) REFERENCES element(id) ON DELETE CASCADE, "
|
||||
" FOREIGN KEY(source_node_id) REFERENCES node(id) ON DELETE CASCADE, "
|
||||
" FOREIGN KEY(target_node_id) REFERENCES node(id) ON DELETE CASCADE"
|
||||
");"
|
||||
);
|
||||
|
||||
executeStatement(
|
||||
"CREATE TABLE IF NOT EXISTS node("
|
||||
" id INTEGER NOT NULL, "
|
||||
" type INTEGER NOT NULL, "
|
||||
" serialized_name TEXT, "
|
||||
" PRIMARY KEY(id), "
|
||||
" FOREIGN KEY(id) REFERENCES element(id) ON DELETE CASCADE"
|
||||
");"
|
||||
);
|
||||
|
||||
executeStatement(
|
||||
"CREATE TABLE IF NOT EXISTS symbol("
|
||||
" id INTEGER NOT NULL, "
|
||||
" definition_kind INTEGER NOT NULL, "
|
||||
" PRIMARY KEY(id), "
|
||||
" FOREIGN KEY(id) REFERENCES node(id) ON DELETE CASCADE"
|
||||
");"
|
||||
);
|
||||
|
||||
executeStatement(
|
||||
"CREATE TABLE IF NOT EXISTS file("
|
||||
" id INTEGER NOT NULL, "
|
||||
" path TEXT, "
|
||||
" modification_time TEXT, "
|
||||
" indexed INTEGER, "
|
||||
" complete INTEGER, "
|
||||
" line_count INTEGER, "
|
||||
" PRIMARY KEY(id), "
|
||||
" FOREIGN KEY(id) REFERENCES node(id) ON DELETE CASCADE"
|
||||
");"
|
||||
);
|
||||
|
||||
executeStatement(
|
||||
"CREATE TABLE IF NOT EXISTS filecontent("
|
||||
" id INTERGER, "
|
||||
" content TEXT, "
|
||||
" FOREIGN KEY(id) REFERENCES file(id)"
|
||||
" ON DELETE CASCADE "
|
||||
" ON UPDATE CASCADE"
|
||||
");"
|
||||
);
|
||||
|
||||
executeStatement(
|
||||
"CREATE TABLE IF NOT EXISTS local_symbol("
|
||||
" id INTEGER NOT NULL, "
|
||||
" name TEXT, "
|
||||
" PRIMARY KEY(id), "
|
||||
" FOREIGN KEY(id) REFERENCES element(id) ON DELETE CASCADE"
|
||||
");"
|
||||
);
|
||||
|
||||
executeStatement(
|
||||
"CREATE TABLE IF NOT EXISTS source_location("
|
||||
" id INTEGER NOT NULL, "
|
||||
" file_node_id INTEGER, "
|
||||
" start_line INTEGER, "
|
||||
" start_column INTEGER, "
|
||||
" end_line INTEGER, "
|
||||
" end_column INTEGER, "
|
||||
" type INTEGER, "
|
||||
" PRIMARY KEY(id), "
|
||||
" FOREIGN KEY(file_node_id) REFERENCES node(id) ON DELETE CASCADE"
|
||||
");"
|
||||
);
|
||||
|
||||
executeStatement(
|
||||
"CREATE TABLE IF NOT EXISTS occurrence("
|
||||
" element_id INTEGER NOT NULL, "
|
||||
" source_location_id INTEGER NOT NULL, "
|
||||
" PRIMARY KEY(element_id, source_location_id), "
|
||||
" FOREIGN KEY(element_id) REFERENCES element(id) ON DELETE CASCADE, "
|
||||
" FOREIGN KEY(source_location_id) REFERENCES source_location(id) ON DELETE CASCADE"
|
||||
");"
|
||||
);
|
||||
|
||||
executeStatement(
|
||||
"CREATE TABLE IF NOT EXISTS component_access("
|
||||
" node_id INTEGER NOT NULL, "
|
||||
" type INTEGER NOT NULL, "
|
||||
" PRIMARY KEY(node_id), "
|
||||
" FOREIGN KEY(node_id) REFERENCES node(id) ON DELETE CASCADE"
|
||||
");"
|
||||
);
|
||||
|
||||
executeStatement(
|
||||
"CREATE TABLE IF NOT EXISTS error("
|
||||
" id INTEGER NOT NULL, "
|
||||
" message TEXT, "
|
||||
" fatal INTEGER NOT NULL, "
|
||||
" indexed INTEGER NOT NULL, "
|
||||
" translation_unit TEXT, "
|
||||
" PRIMARY KEY(id), "
|
||||
" FOREIGN KEY(id) REFERENCES element(id) ON DELETE CASCADE"
|
||||
");"
|
||||
);
|
||||
|
||||
insertOrUpdateMetaValue("storage_version", std::to_string(getSupportedDatabaseVersion()));
|
||||
}
|
||||
|
||||
void DatabaseStorage::clearTables()
|
||||
{
|
||||
executeStatement("PRAGMA foreign_keys=OFF;");
|
||||
|
||||
const std::vector<std::string> tableNames = {
|
||||
"meta",
|
||||
"error"
|
||||
"component_access",
|
||||
"occurrence",
|
||||
"source_location",
|
||||
"local_symbol",
|
||||
"filecontent",
|
||||
"file",
|
||||
"symbol",
|
||||
"node",
|
||||
"edge",
|
||||
"element"
|
||||
};
|
||||
|
||||
for (const std::string& tableName : tableNames)
|
||||
{
|
||||
executeStatement("DROP TABLE IF EXISTS main." + tableName + ";");
|
||||
}
|
||||
|
||||
setupTables();
|
||||
}
|
||||
|
||||
bool DatabaseStorage::isEmpty() const
|
||||
{
|
||||
const std::string tableName = "meta";
|
||||
CppSQLite3Query q = executeQuery(
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name='" + tableName + "';"
|
||||
);
|
||||
|
||||
if (!q.eof())
|
||||
{
|
||||
return q.getStringField(0, "") == tableName;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DatabaseStorage::isCompatible() const
|
||||
{
|
||||
if (isEmpty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return getLoadedDatabaseVersion() == getSupportedDatabaseVersion();
|
||||
}
|
||||
|
||||
int DatabaseStorage::getLoadedDatabaseVersion() const
|
||||
{
|
||||
if (isEmpty())
|
||||
{
|
||||
throw SourcetrailException("Unable to determine version of an empty database.");
|
||||
}
|
||||
|
||||
CppSQLite3Query q = executeQuery("SELECT value FROM meta WHERE key = 'storage_version';");
|
||||
if (!q.eof())
|
||||
{
|
||||
return std::stoi(q.getStringField(0, "0"));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DatabaseStorage::beginTransaction()
|
||||
{
|
||||
executeStatement("BEGIN TRANSACTION;");
|
||||
}
|
||||
|
||||
void DatabaseStorage::commitTransaction()
|
||||
{
|
||||
executeStatement("COMMIT TRANSACTION;");
|
||||
}
|
||||
|
||||
void DatabaseStorage::rollbackTransaction()
|
||||
{
|
||||
executeStatement("ROLLBACK TRANSACTION;");
|
||||
}
|
||||
|
||||
void DatabaseStorage::optimizeDatabaseMemory()
|
||||
{
|
||||
executeStatement("VACUUM;");
|
||||
}
|
||||
|
||||
int DatabaseStorage::addNode(const std::string& serializedNameHierarchy)
|
||||
{
|
||||
int id = 0;
|
||||
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"SELECT id FROM node WHERE serialized_name == ? LIMIT 1;"
|
||||
);
|
||||
stmt.bind(1, serializedNameHierarchy.c_str());
|
||||
CppSQLite3Query q = executeQuery(stmt);
|
||||
if (!q.eof())
|
||||
{
|
||||
id = q.getIntField(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (id == 0)
|
||||
{
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"INSERT INTO element(id) VALUES(NULL);"
|
||||
);
|
||||
executeStatement(stmt);
|
||||
id = m_database.lastRowId();
|
||||
}
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"INSERT INTO node(id, type, serialized_name) VALUES(?, ?, ?);"
|
||||
);
|
||||
stmt.bind(1, id);
|
||||
stmt.bind(2, nodeKindToInt(NODE_UNKNOWN));
|
||||
stmt.bind(3, serializedNameHierarchy.c_str());
|
||||
executeStatement(stmt);
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
void DatabaseStorage::addSymbol(int nodeId, int definitionKind)
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"INSERT OR IGNORE INTO symbol(id, definition_kind) VALUES(?, ?);"
|
||||
);
|
||||
stmt.bind(1, nodeId);
|
||||
stmt.bind(2, definitionKind);
|
||||
executeStatement(stmt);
|
||||
}
|
||||
|
||||
|
||||
void DatabaseStorage::addFile(int nodeId, const std::string& filePath)
|
||||
{
|
||||
std::string modificationTime = utility::getDateTimeString(0);
|
||||
const bool indexed = true;
|
||||
const bool complete = true;
|
||||
std::string content = "";
|
||||
if (utility::getFileExists(filePath))
|
||||
{
|
||||
modificationTime = utility::getDateTimeString(utility::getFileModificationTime(filePath));
|
||||
content = utility::getFileContent(filePath);
|
||||
}
|
||||
const int lineCount = utility::getLineCount(content);
|
||||
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"INSERT OR IGNORE INTO file(id, path, modification_time, indexed, complete, line_count) VALUES(?, ?, ?, ?, ?, ?);"
|
||||
);
|
||||
stmt.bind(1, nodeId);
|
||||
stmt.bind(2, filePath.c_str());
|
||||
stmt.bind(3, modificationTime.c_str());
|
||||
stmt.bind(4, indexed);
|
||||
stmt.bind(5, complete);
|
||||
stmt.bind(6, lineCount);
|
||||
executeStatement(stmt);
|
||||
}
|
||||
|
||||
if (!content.empty())
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"INSERT INTO filecontent(id, content) VALUES(?, ?);"
|
||||
);
|
||||
stmt.bind(1, nodeId);
|
||||
stmt.bind(2, content.c_str());
|
||||
executeStatement(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
int DatabaseStorage::addEdge(int sourceNodeId, int targetNodeId, int edgeKind)
|
||||
{
|
||||
int id = 0;
|
||||
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"SELECT id FROM edge WHERE source_node_id == ? AND target_node_id == ? AND type == ? LIMIT 1;"
|
||||
);
|
||||
stmt.bind(1, sourceNodeId);
|
||||
stmt.bind(2, targetNodeId);
|
||||
stmt.bind(3, edgeKind);
|
||||
CppSQLite3Query q = executeQuery(stmt);
|
||||
if (!q.eof())
|
||||
{
|
||||
id = q.getIntField(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (id == 0)
|
||||
{
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"INSERT INTO element(id) VALUES(NULL);"
|
||||
);
|
||||
executeStatement(stmt);
|
||||
id = m_database.lastRowId();
|
||||
}
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"INSERT INTO edge(id, type, source_node_id, target_node_id) VALUES(?, ?, ?, ?);"
|
||||
);
|
||||
stmt.bind(1, id);
|
||||
stmt.bind(2, edgeKind);
|
||||
stmt.bind(3, sourceNodeId);
|
||||
stmt.bind(4, targetNodeId);
|
||||
executeStatement(stmt);
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
int DatabaseStorage::addLocalSymbol(const std::string& name)
|
||||
{
|
||||
int id = 0;
|
||||
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"SELECT id FROM local_symbol WHERE name == ? LIMIT 1;"
|
||||
);
|
||||
stmt.bind(1, name.c_str());
|
||||
CppSQLite3Query q = executeQuery(stmt);
|
||||
if (!q.eof())
|
||||
{
|
||||
id = q.getIntField(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (id == 0)
|
||||
{
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"INSERT INTO element(id) VALUES(NULL);"
|
||||
);
|
||||
executeStatement(stmt);
|
||||
id = m_database.lastRowId();
|
||||
}
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"INSERT INTO local_symbol(id, name) VALUES(?, ?);"
|
||||
);
|
||||
stmt.bind(1, id);
|
||||
stmt.bind(2, name.c_str());
|
||||
executeStatement(stmt);
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
int DatabaseStorage::addSourceLocation(
|
||||
int fileId,
|
||||
int startLineNumber,
|
||||
int startColumnNumber,
|
||||
int endLineNumber,
|
||||
int endColumnNumber,
|
||||
int locationKind)
|
||||
{
|
||||
int id = 0;
|
||||
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"SELECT id FROM source_location WHERE "
|
||||
"file_node_id = ? AND "
|
||||
"start_line = ? AND "
|
||||
"start_column = ? AND "
|
||||
"end_line = ? AND "
|
||||
"end_column = ? AND "
|
||||
"type = ? "
|
||||
"LIMIT 1;"
|
||||
);
|
||||
stmt.bind(1, fileId);
|
||||
stmt.bind(2, startLineNumber);
|
||||
stmt.bind(3, startColumnNumber);
|
||||
stmt.bind(4, endLineNumber);
|
||||
stmt.bind(5, endColumnNumber);
|
||||
stmt.bind(6, locationKind);
|
||||
CppSQLite3Query q = executeQuery(stmt);
|
||||
if (!q.eof())
|
||||
{
|
||||
id = q.getIntField(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (id == 0)
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"INSERT INTO source_location(id, file_node_id, start_line, start_column, end_line, end_column, type) VALUES(NULL, ?, ?, ?, ?, ?, ?);"
|
||||
);
|
||||
stmt.bind(1, fileId);
|
||||
stmt.bind(2, startLineNumber);
|
||||
stmt.bind(3, startColumnNumber);
|
||||
stmt.bind(4, endLineNumber);
|
||||
stmt.bind(5, endColumnNumber);
|
||||
stmt.bind(6, locationKind);
|
||||
executeStatement(stmt);
|
||||
id = m_database.lastRowId();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
void DatabaseStorage::addOccurrence(int elementId, int sourceLocationId)
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"INSERT OR IGNORE INTO occurrence(element_id, source_location_id) VALUES(?, ?);"
|
||||
);
|
||||
stmt.bind(1, elementId);
|
||||
stmt.bind(2, sourceLocationId);
|
||||
executeStatement(stmt);
|
||||
}
|
||||
|
||||
int DatabaseStorage::addError(
|
||||
const std::string& message,
|
||||
bool fatal)
|
||||
{
|
||||
int id = 0;
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"SELECT id FROM error WHERE "
|
||||
"message = ? AND "
|
||||
"fatal == ? "
|
||||
"LIMIT 1;"
|
||||
);
|
||||
stmt.bind(1, message.c_str());
|
||||
stmt.bind(2, fatal);
|
||||
CppSQLite3Query q = executeQuery(stmt);
|
||||
if (!q.eof() && q.numFields() > 0)
|
||||
{
|
||||
id = q.getIntField(0, -1);
|
||||
}
|
||||
}
|
||||
|
||||
if (id == 0)
|
||||
{
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"INSERT INTO element(id) VALUES(NULL);"
|
||||
);
|
||||
executeStatement(stmt);
|
||||
id = m_database.lastRowId();
|
||||
}
|
||||
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"INSERT INTO error(id, message, fatal, indexed, translation_unit) "
|
||||
"VALUES(?, ?, ?, ?, ?);"
|
||||
);
|
||||
stmt.bind(1, id);
|
||||
stmt.bind(2, message.c_str());
|
||||
stmt.bind(3, fatal);
|
||||
stmt.bind(4, true);
|
||||
stmt.bind(5, "");
|
||||
executeStatement(stmt);
|
||||
id = m_database.lastRowId();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
void DatabaseStorage::setNodeType(int nodeId, int nodeType)
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(
|
||||
"UPDATE node SET type = ? WHERE id == ?;"
|
||||
);
|
||||
stmt.bind(1, nodeType);
|
||||
stmt.bind(2, nodeId);
|
||||
executeStatement(stmt);
|
||||
}
|
||||
|
||||
// --- Private Interface ---
|
||||
|
||||
void DatabaseStorage::insertOrUpdateMetaValue(const std::string& key, const std::string& value)
|
||||
{
|
||||
CppSQLite3Statement stmt = m_database.compileStatement(std::string(
|
||||
"INSERT OR REPLACE INTO meta(id, key, value) VALUES("
|
||||
"(SELECT id FROM meta WHERE key = ?), ?, ?"
|
||||
");"
|
||||
).c_str());
|
||||
|
||||
stmt.bind(1, key.c_str());
|
||||
stmt.bind(2, key.c_str());
|
||||
stmt.bind(3, value.c_str());
|
||||
executeStatement(stmt);
|
||||
}
|
||||
|
||||
void DatabaseStorage::executeStatement(const std::string& statement) const
|
||||
{
|
||||
try
|
||||
{
|
||||
m_database.execDML(statement.c_str());
|
||||
}
|
||||
catch (CppSQLite3Exception e)
|
||||
{
|
||||
throw SourcetrailException("Failed to execute statement \"" + statement + "\" with message \"" + e.errorMessage() + "\".");
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseStorage::executeStatement(CppSQLite3Statement& statement) const
|
||||
{
|
||||
try
|
||||
{
|
||||
statement.execDML();
|
||||
}
|
||||
catch (CppSQLite3Exception e)
|
||||
{
|
||||
throw SourcetrailException("Failed to execute statement with message \"" + std::string(e.errorMessage()) + "\".");
|
||||
}
|
||||
}
|
||||
|
||||
CppSQLite3Query DatabaseStorage::executeQuery(const std::string& query) const
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_database.execQuery(query.c_str());
|
||||
}
|
||||
catch (CppSQLite3Exception e)
|
||||
{
|
||||
throw SourcetrailException("Failed to execute query \"" + query + "\" with message \"" + e.errorMessage() + "\".");
|
||||
}
|
||||
}
|
||||
|
||||
CppSQLite3Query DatabaseStorage::executeQuery(CppSQLite3Statement& statement) const
|
||||
{
|
||||
try
|
||||
{
|
||||
return statement.execQuery();
|
||||
}
|
||||
catch (CppSQLite3Exception e)
|
||||
{
|
||||
throw SourcetrailException("Failed to execute query with message \"" + std::string(e.errorMessage()) + "\".");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user