911 lines
25 KiB
C++
911 lines
25 KiB
C++
/*
|
|
* Copyright 2018 Coati Software KG
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "DatabaseStorage.h"
|
|
|
|
#include <vector>
|
|
|
|
#include "NodeKind.h"
|
|
#include "SourcetrailException.h"
|
|
#include "StorageFile.h"
|
|
#include "StorageNode.h"
|
|
#include "StorageSymbol.h"
|
|
#include "utility.h"
|
|
#include "version.h"
|
|
|
|
namespace sourcetrail
|
|
{
|
|
// --- Public Interface ---
|
|
|
|
int DatabaseStorage::getSupportedDatabaseVersion()
|
|
{
|
|
return DATABASE_VERSION;
|
|
}
|
|
|
|
std::unique_ptr<DatabaseStorage> DatabaseStorage::openDatabase(const std::string& dbFilePath)
|
|
{
|
|
try
|
|
{
|
|
std::unique_ptr<DatabaseStorage> storage = std::unique_ptr<DatabaseStorage>(new DatabaseStorage());
|
|
storage->m_database.open(dbFilePath.c_str());
|
|
storage->executeStatement("PRAGMA foreign_keys=ON;");
|
|
return std::move(storage);
|
|
}
|
|
catch (CppSQLite3Exception e)
|
|
{
|
|
throw SourcetrailException(e.errorMessage());
|
|
}
|
|
}
|
|
|
|
DatabaseStorage::~DatabaseStorage()
|
|
{
|
|
clearPrecompiledStatements();
|
|
|
|
m_database.close();
|
|
}
|
|
|
|
void DatabaseStorage::setupDatabase()
|
|
{
|
|
executeStatement("PRAGMA foreign_keys=ON;");
|
|
|
|
if (!isCompatible())
|
|
{
|
|
throw SourcetrailException("Unable to setup database tables because database is not compatible.");
|
|
}
|
|
|
|
setupTables();
|
|
|
|
setupIndices();
|
|
|
|
setupPrecompiledStatements();
|
|
|
|
insertOrUpdateMetaValue("storage_version", std::to_string(getSupportedDatabaseVersion()));
|
|
}
|
|
|
|
void DatabaseStorage::clearDatabase()
|
|
{
|
|
executeStatement("PRAGMA foreign_keys=OFF;");
|
|
|
|
clearPrecompiledStatements();
|
|
|
|
clearTables();
|
|
|
|
setupDatabase();
|
|
}
|
|
|
|
void DatabaseStorage::setProjectSettingsText(const std::string& text)
|
|
{
|
|
insertOrUpdateMetaValue("project_settings", text);
|
|
}
|
|
|
|
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::addElementComponent(const StorageElementComponentData& storageElementComponentData)
|
|
{
|
|
m_insertElementComponentStatement.bind(1, storageElementComponentData.elementId);
|
|
m_insertElementComponentStatement.bind(2, storageElementComponentData.componentKind);
|
|
m_insertElementComponentStatement.bind(3, storageElementComponentData.data.c_str());
|
|
executeStatement(m_insertElementComponentStatement);
|
|
const int id = static_cast<int>(m_database.lastRowId());
|
|
m_insertElementComponentStatement.reset();
|
|
return id;
|
|
}
|
|
|
|
int DatabaseStorage::addNode(const StorageNodeData& storageNodeData)
|
|
{
|
|
int id = 0;
|
|
|
|
{
|
|
m_findNodeStatement.bind(1, storageNodeData.serializedName.c_str());
|
|
CppSQLite3Query q = executeQuery(m_findNodeStatement);
|
|
if (!q.eof())
|
|
{
|
|
id = q.getIntField(0, 0);
|
|
}
|
|
m_findNodeStatement.reset();
|
|
}
|
|
|
|
// FIXME: update node nodeKind here
|
|
|
|
if (id == 0)
|
|
{
|
|
id = insertElement();
|
|
|
|
m_insertNodeStatement.bind(1, id);
|
|
m_insertNodeStatement.bind(2, storageNodeData.nodeKind);
|
|
m_insertNodeStatement.bind(3, storageNodeData.serializedName.c_str());
|
|
executeStatement(m_insertNodeStatement);
|
|
m_insertNodeStatement.reset();
|
|
}
|
|
return id;
|
|
}
|
|
|
|
void DatabaseStorage::addSymbol(const StorageSymbol& storageSymbol)
|
|
{
|
|
m_insertSymbolStatement.bind(1, storageSymbol.id);
|
|
m_insertSymbolStatement.bind(2, storageSymbol.definitionKind);
|
|
executeStatement(m_insertSymbolStatement);
|
|
m_insertSymbolStatement.reset();
|
|
}
|
|
|
|
void DatabaseStorage::addFile(const StorageFile& storageFile)
|
|
{
|
|
{
|
|
m_findFileStatement.bind(1, storageFile.id);
|
|
CppSQLite3Query q = executeQuery(m_findFileStatement);
|
|
bool exists = !q.eof();
|
|
m_findFileStatement.reset();
|
|
if (exists)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
std::string content = "";
|
|
if (utility::getFileExists(storageFile.filePath))
|
|
{
|
|
content = utility::getFileContent(storageFile.filePath);
|
|
}
|
|
const int lineCount = utility::getLineCount(content);
|
|
|
|
{
|
|
m_insertFileStatement.bind(1, storageFile.id);
|
|
m_insertFileStatement.bind(2, storageFile.filePath.c_str());
|
|
m_insertFileStatement.bind(3, storageFile.languageIdentifier.c_str());
|
|
m_insertFileStatement.bind(4, storageFile.modificationTime.c_str());
|
|
m_insertFileStatement.bind(5, storageFile.indexed);
|
|
m_insertFileStatement.bind(6, storageFile.complete);
|
|
m_insertFileStatement.bind(7, lineCount);
|
|
executeStatement(m_insertFileStatement);
|
|
m_insertFileStatement.reset();
|
|
}
|
|
|
|
if (!content.empty())
|
|
{
|
|
m_insertFileContentStatement.bind(1, storageFile.id);
|
|
m_insertFileContentStatement.bind(2, content.c_str());
|
|
executeStatement(m_insertFileContentStatement);
|
|
m_insertFileContentStatement.reset();
|
|
}
|
|
}
|
|
|
|
int DatabaseStorage::addEdge(const StorageEdgeData& storageEdgeData)
|
|
{
|
|
int id = 0;
|
|
|
|
{
|
|
m_findEdgeStatement.bind(1, storageEdgeData.sourceNodeId);
|
|
m_findEdgeStatement.bind(2, storageEdgeData.targetNodeId);
|
|
m_findEdgeStatement.bind(3, storageEdgeData.edgeKind);
|
|
CppSQLite3Query q = executeQuery(m_findEdgeStatement);
|
|
if (!q.eof())
|
|
{
|
|
id = q.getIntField(0, 0);
|
|
}
|
|
m_findEdgeStatement.reset();
|
|
}
|
|
|
|
if (id == 0)
|
|
{
|
|
id = insertElement();
|
|
|
|
m_insertEdgeStatement.bind(1, id);
|
|
m_insertEdgeStatement.bind(2, storageEdgeData.edgeKind);
|
|
m_insertEdgeStatement.bind(3, storageEdgeData.sourceNodeId);
|
|
m_insertEdgeStatement.bind(4, storageEdgeData.targetNodeId);
|
|
executeStatement(m_insertEdgeStatement);
|
|
m_insertEdgeStatement.reset();
|
|
}
|
|
return id;
|
|
}
|
|
|
|
int DatabaseStorage::addLocalSymbol(const StorageLocalSymbolData& storageLocalSymbolData)
|
|
{
|
|
int id = 0;
|
|
|
|
{
|
|
m_findLocalSymbolStmt.bind(1, storageLocalSymbolData.name.c_str());
|
|
CppSQLite3Query q = executeQuery(m_findLocalSymbolStmt);
|
|
if (!q.eof())
|
|
{
|
|
id = q.getIntField(0, 0);
|
|
}
|
|
m_findLocalSymbolStmt.reset();
|
|
}
|
|
|
|
if (id == 0)
|
|
{
|
|
id = insertElement();
|
|
|
|
m_insertLocalSymbolStmt.bind(1, id);
|
|
m_insertLocalSymbolStmt.bind(2, storageLocalSymbolData.name.c_str());
|
|
executeStatement(m_insertLocalSymbolStmt);
|
|
m_insertLocalSymbolStmt.reset();
|
|
}
|
|
return id;
|
|
}
|
|
|
|
int DatabaseStorage::addSourceLocation(const StorageSourceLocationData& storageSourceLocationData)
|
|
{
|
|
int id = 0;
|
|
|
|
{
|
|
m_findSourceLocationStmt.bind(1, storageSourceLocationData.fileNodeId);
|
|
m_findSourceLocationStmt.bind(2, storageSourceLocationData.startLineNumber);
|
|
m_findSourceLocationStmt.bind(3, storageSourceLocationData.startColumnNumber);
|
|
m_findSourceLocationStmt.bind(4, storageSourceLocationData.endLineNumber);
|
|
m_findSourceLocationStmt.bind(5, storageSourceLocationData.endColumnNumber);
|
|
m_findSourceLocationStmt.bind(6, storageSourceLocationData.locationKind);
|
|
CppSQLite3Query q = executeQuery(m_findSourceLocationStmt);
|
|
if (!q.eof())
|
|
{
|
|
id = q.getIntField(0, 0);
|
|
}
|
|
m_findSourceLocationStmt.reset();
|
|
}
|
|
|
|
if (id == 0)
|
|
{
|
|
m_insertSourceLocationStmt.bind(1, storageSourceLocationData.fileNodeId);
|
|
m_insertSourceLocationStmt.bind(2, storageSourceLocationData.startLineNumber);
|
|
m_insertSourceLocationStmt.bind(3, storageSourceLocationData.startColumnNumber);
|
|
m_insertSourceLocationStmt.bind(4, storageSourceLocationData.endLineNumber);
|
|
m_insertSourceLocationStmt.bind(5, storageSourceLocationData.endColumnNumber);
|
|
m_insertSourceLocationStmt.bind(6, storageSourceLocationData.locationKind);
|
|
executeStatement(m_insertSourceLocationStmt);
|
|
id = static_cast<int>(m_database.lastRowId());
|
|
m_insertSourceLocationStmt.reset();
|
|
}
|
|
return id;
|
|
}
|
|
|
|
void DatabaseStorage::addOccurrence(const StorageOccurrence& storageOccurrence)
|
|
{
|
|
m_insertOccurenceStmt.bind(1, storageOccurrence.elementId);
|
|
m_insertOccurenceStmt.bind(2, storageOccurrence.sourceLocationId);
|
|
executeStatement(m_insertOccurenceStmt);
|
|
m_insertOccurenceStmt.reset();
|
|
}
|
|
|
|
int DatabaseStorage::addError(const StorageErrorData& storageErrorData)
|
|
{
|
|
int id = 0;
|
|
{
|
|
m_findErrorStatement.bind(1, storageErrorData.message.c_str());
|
|
m_findErrorStatement.bind(2, storageErrorData.fatal);
|
|
CppSQLite3Query q = executeQuery(m_findErrorStatement);
|
|
if (!q.eof() && q.numFields() > 0)
|
|
{
|
|
id = q.getIntField(0, -1);
|
|
}
|
|
m_findErrorStatement.reset();
|
|
}
|
|
|
|
if (id == 0)
|
|
{
|
|
id = insertElement();
|
|
|
|
m_insertErrorStatement.bind(1, id);
|
|
m_insertErrorStatement.bind(2, storageErrorData.message.c_str());
|
|
m_insertErrorStatement.bind(3, storageErrorData.fatal);
|
|
m_insertErrorStatement.bind(4, storageErrorData.indexed);
|
|
m_insertErrorStatement.bind(5, storageErrorData.translationUnit.c_str());
|
|
executeStatement(m_insertErrorStatement);
|
|
id = static_cast<int>(m_database.lastRowId());
|
|
m_insertErrorStatement.reset();
|
|
}
|
|
return id;
|
|
}
|
|
|
|
void DatabaseStorage::setNodeType(int nodeId, int nodeType)
|
|
{
|
|
m_setNodeTypeStmt.bind(1, nodeType);
|
|
m_setNodeTypeStmt.bind(2, nodeId);
|
|
executeStatement(m_setNodeTypeStmt);
|
|
m_setNodeTypeStmt.reset();
|
|
}
|
|
|
|
void DatabaseStorage::setFileLanguage(int fileId, const std::string& languageIdentifier)
|
|
{
|
|
m_setFileLanguageStmt.bind(1, languageIdentifier.c_str());
|
|
m_setFileLanguageStmt.bind(2, fileId);
|
|
executeStatement(m_setFileLanguageStmt);
|
|
m_setFileLanguageStmt.reset();
|
|
}
|
|
|
|
// --- Private Interface ---
|
|
|
|
void DatabaseStorage::setupTables()
|
|
{
|
|
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 element_component("
|
|
" id INTEGER, "
|
|
" element_id INTEGER, "
|
|
" type INTEGER, "
|
|
" data TEXT, "
|
|
" PRIMARY KEY(id), "
|
|
" FOREIGN KEY(element_id) REFERENCES element(id) ON DELETE CASCADE"
|
|
");");
|
|
|
|
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, "
|
|
" language 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"
|
|
");");
|
|
}
|
|
|
|
void DatabaseStorage::clearTables()
|
|
{
|
|
const std::vector<std::string> tableNames = {
|
|
"meta",
|
|
"error",
|
|
"component_access",
|
|
"occurrence",
|
|
"source_location",
|
|
"local_symbol",
|
|
"filecontent",
|
|
"file",
|
|
"symbol",
|
|
"node",
|
|
"edge",
|
|
"element_component"
|
|
"element"};
|
|
|
|
for (const std::string& tableName: tableNames)
|
|
{
|
|
executeStatement("DROP TABLE IF EXISTS main." + tableName + ";");
|
|
}
|
|
}
|
|
|
|
void DatabaseStorage::setupIndices()
|
|
{
|
|
executeStatement("CREATE INDEX IF NOT EXISTS node_serialized_name_index ON node(serialized_name);");
|
|
|
|
executeStatement("CREATE INDEX IF NOT EXISTS edge_source_target_type_index ON edge(source_node_id, target_node_id, type);");
|
|
|
|
executeStatement("CREATE INDEX IF NOT EXISTS local_symbol_name_index ON local_symbol(name);");
|
|
|
|
executeStatement(
|
|
"CREATE INDEX IF NOT EXISTS source_location_all_data_index "
|
|
"ON source_location(file_node_id, start_line, start_column, end_line, end_column, type);");
|
|
|
|
executeStatement("CREATE INDEX IF NOT EXISTS error_all_data_index ON error(message, fatal);");
|
|
}
|
|
|
|
void DatabaseStorage::setupPrecompiledStatements()
|
|
{
|
|
m_insertElementStatement = compileStatement("INSERT INTO element(id) VALUES(NULL);");
|
|
|
|
m_insertElementComponentStatement = compileStatement(
|
|
"INSERT INTO element_component(id, element_id, type, data) VALUES(NULL, ?, ?, ?);");
|
|
|
|
m_findNodeStatement = compileStatement("SELECT id FROM node WHERE serialized_name == ? LIMIT 1;");
|
|
|
|
m_insertNodeStatement = compileStatement("INSERT INTO node(id, type, serialized_name) VALUES(?, ?, ?);");
|
|
|
|
m_setNodeTypeStmt = compileStatement("UPDATE node SET type = ? WHERE id == ?;");
|
|
|
|
m_insertSymbolStatement = compileStatement("INSERT OR IGNORE INTO symbol(id, definition_kind) VALUES(?, ?);");
|
|
|
|
m_findFileStatement = compileStatement("SELECT id FROM file WHERE id == ?;");
|
|
|
|
m_insertFileStatement = compileStatement(
|
|
"INSERT OR IGNORE INTO file(id, path, language, modification_time, indexed, complete, line_count) VALUES(?, ?, ?, ?, ?, ?, ?);");
|
|
|
|
m_setFileLanguageStmt = compileStatement("UPDATE file SET language = ? WHERE id == ?;");
|
|
|
|
m_insertFileContentStatement = compileStatement("INSERT INTO filecontent(id, content) VALUES(?, ?);");
|
|
|
|
m_findEdgeStatement = compileStatement("SELECT id FROM edge WHERE source_node_id == ? AND target_node_id == ? AND type == ? LIMIT 1;");
|
|
|
|
m_insertEdgeStatement = compileStatement("INSERT INTO edge(id, type, source_node_id, target_node_id) VALUES(?, ?, ?, ?);");
|
|
|
|
m_findLocalSymbolStmt = compileStatement("SELECT id FROM local_symbol WHERE name == ? LIMIT 1;");
|
|
|
|
m_insertLocalSymbolStmt = compileStatement("INSERT INTO local_symbol(id, name) VALUES(?, ?);");
|
|
|
|
m_findSourceLocationStmt = 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;");
|
|
|
|
m_insertSourceLocationStmt = compileStatement(
|
|
"INSERT INTO source_location("
|
|
"id, file_node_id, start_line, start_column, end_line, end_column, type) "
|
|
"VALUES(NULL, ?, ?, ?, ?, ?, ?);");
|
|
|
|
m_insertOccurenceStmt = compileStatement("INSERT OR IGNORE INTO occurrence(element_id, source_location_id) VALUES(?, ?);");
|
|
|
|
m_findErrorStatement = compileStatement(
|
|
"SELECT id FROM error WHERE "
|
|
"message = ? AND "
|
|
"fatal == ? "
|
|
"LIMIT 1;");
|
|
|
|
m_insertErrorStatement = compileStatement(
|
|
"INSERT INTO error(id, message, fatal, indexed, translation_unit) "
|
|
"VALUES(?, ?, ?, ?, ?);");
|
|
|
|
m_insertOrUpdateMetaValueStmt = compileStatement(
|
|
"INSERT OR REPLACE INTO meta(id, key, value) VALUES("
|
|
"(SELECT id FROM meta WHERE key = ?), ?, ?"
|
|
");");
|
|
}
|
|
|
|
void DatabaseStorage::clearPrecompiledStatements()
|
|
{
|
|
m_insertElementStatement.finalize();
|
|
m_insertElementComponentStatement.finalize();
|
|
m_findNodeStatement.finalize();
|
|
m_insertNodeStatement.finalize();
|
|
m_setNodeTypeStmt.finalize();
|
|
m_insertSymbolStatement.finalize();
|
|
m_findFileStatement.finalize();
|
|
m_insertFileStatement.finalize();
|
|
m_setFileLanguageStmt.finalize();
|
|
m_insertFileContentStatement.finalize();
|
|
m_findEdgeStatement.finalize();
|
|
m_insertEdgeStatement.finalize();
|
|
m_findLocalSymbolStmt.finalize();
|
|
m_insertLocalSymbolStmt.finalize();
|
|
m_findSourceLocationStmt.finalize();
|
|
m_insertSourceLocationStmt.finalize();
|
|
m_insertOccurenceStmt.finalize();
|
|
m_findErrorStatement.finalize();
|
|
m_insertErrorStatement.finalize();
|
|
m_insertOrUpdateMetaValueStmt.finalize();
|
|
}
|
|
|
|
int DatabaseStorage::insertElement()
|
|
{
|
|
executeStatement(m_insertElementStatement);
|
|
const int id = static_cast<int>(m_database.lastRowId());
|
|
m_insertElementStatement.reset();
|
|
return id;
|
|
}
|
|
|
|
void DatabaseStorage::insertOrUpdateMetaValue(const std::string& key, const std::string& value)
|
|
{
|
|
m_insertOrUpdateMetaValueStmt.bind(1, key.c_str());
|
|
m_insertOrUpdateMetaValueStmt.bind(2, key.c_str());
|
|
m_insertOrUpdateMetaValueStmt.bind(3, value.c_str());
|
|
executeStatement(m_insertOrUpdateMetaValueStmt);
|
|
m_insertOrUpdateMetaValueStmt.reset();
|
|
}
|
|
|
|
CppSQLite3Statement DatabaseStorage::compileStatement(const std::string& statement) const
|
|
{
|
|
try
|
|
{
|
|
return m_database.compileStatement(statement.c_str());
|
|
}
|
|
catch (CppSQLite3Exception e)
|
|
{
|
|
throw SourcetrailException("Failed to compile statement \"" + statement + "\" with message \"" + e.errorMessage() + "\".");
|
|
}
|
|
|
|
return CppSQLite3Statement();
|
|
}
|
|
|
|
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()) + "\".");
|
|
}
|
|
}
|
|
|
|
template <>
|
|
std::vector<StorageEdge> DatabaseStorage::doGetAll<StorageEdge>(const std::string& query) const
|
|
{
|
|
CppSQLite3Query q = executeQuery("SELECT id, type, source_node_id, target_node_id FROM edge " + query + ";");
|
|
|
|
std::vector<StorageEdge> edges;
|
|
while (!q.eof())
|
|
{
|
|
const int id = q.getIntField(0, 0);
|
|
const int edgeKind = q.getIntField(1, -1);
|
|
const int sourceId = q.getIntField(2, 0);
|
|
const int targetId = q.getIntField(3, 0);
|
|
|
|
if (id != 0 && edgeKind != -1)
|
|
{
|
|
edges.emplace_back(StorageEdge(id, sourceId, targetId, edgeKind));
|
|
}
|
|
|
|
q.nextRow();
|
|
}
|
|
return edges;
|
|
}
|
|
|
|
template <>
|
|
std::vector<StorageNode> DatabaseStorage::doGetAll<StorageNode>(const std::string& query) const
|
|
{
|
|
CppSQLite3Query q = executeQuery("SELECT id, type, serialized_name FROM node " + query + ";");
|
|
|
|
std::vector<StorageNode> nodes;
|
|
while (!q.eof())
|
|
{
|
|
const int id = q.getIntField(0, 0);
|
|
const int type = q.getIntField(1, -1);
|
|
const std::string serializedName = q.getStringField(2, "");
|
|
|
|
if (id != 0 && type != -1)
|
|
{
|
|
nodes.emplace_back(StorageNode(id, type, serializedName));
|
|
}
|
|
|
|
q.nextRow();
|
|
}
|
|
return nodes;
|
|
}
|
|
|
|
template <>
|
|
std::vector<StorageSymbol> DatabaseStorage::doGetAll<StorageSymbol>(const std::string& query) const
|
|
{
|
|
CppSQLite3Query q = executeQuery("SELECT id, definition_kind FROM symbol " + query + ";");
|
|
|
|
std::vector<StorageSymbol> symbols;
|
|
while (!q.eof())
|
|
{
|
|
const int id = q.getIntField(0, 0);
|
|
const int definitionKind = q.getIntField(1, 0);
|
|
|
|
if (id != 0)
|
|
{
|
|
symbols.emplace_back(StorageSymbol(id, definitionKind));
|
|
}
|
|
|
|
q.nextRow();
|
|
}
|
|
return symbols;
|
|
}
|
|
|
|
template <>
|
|
std::vector<StorageFile> DatabaseStorage::doGetAll<StorageFile>(const std::string& query) const
|
|
{
|
|
CppSQLite3Query q = executeQuery("SELECT id, path, language, modification_time, indexed, complete FROM file " + query + ";");
|
|
|
|
std::vector<StorageFile> files;
|
|
while (!q.eof())
|
|
{
|
|
const int id = q.getIntField(0, 0);
|
|
const std::string filePath = q.getStringField(1, "");
|
|
const std::string languageIdentifier = q.getStringField(2, "");
|
|
const std::string modificationTime = q.getStringField(3, "");
|
|
const bool indexed = q.getIntField(4, 0);
|
|
const bool complete = q.getIntField(5, 0);
|
|
|
|
if (id != 0)
|
|
{
|
|
files.emplace_back(StorageFile(id, filePath, languageIdentifier, modificationTime, indexed, complete));
|
|
}
|
|
q.nextRow();
|
|
}
|
|
|
|
return files;
|
|
}
|
|
|
|
template <>
|
|
std::vector<StorageLocalSymbol> DatabaseStorage::doGetAll<StorageLocalSymbol>(const std::string& query) const
|
|
{
|
|
CppSQLite3Query q = executeQuery("SELECT id, name FROM local_symbol " + query + ";");
|
|
|
|
std::vector<StorageLocalSymbol> localSymbols;
|
|
|
|
while (!q.eof())
|
|
{
|
|
const int id = q.getIntField(0, 0);
|
|
const std::string name = q.getStringField(1, "");
|
|
|
|
if (id != 0)
|
|
{
|
|
localSymbols.emplace_back(StorageLocalSymbol(id, name));
|
|
}
|
|
|
|
q.nextRow();
|
|
}
|
|
return localSymbols;
|
|
}
|
|
|
|
template <>
|
|
std::vector<StorageSourceLocation> DatabaseStorage::doGetAll<StorageSourceLocation>(const std::string& query) const
|
|
{
|
|
CppSQLite3Query q = executeQuery(
|
|
"SELECT id, file_node_id, start_line, start_column, end_line, end_column, type FROM source_location " + query + ";");
|
|
|
|
std::vector<StorageSourceLocation> sourceLocations;
|
|
|
|
while (!q.eof())
|
|
{
|
|
const int id = q.getIntField(0, 0);
|
|
const int fileNodeId = q.getIntField(1, 0);
|
|
const int startLineNumber = q.getIntField(2, -1);
|
|
const int startColNumber = q.getIntField(3, -1);
|
|
const int endLineNumber = q.getIntField(4, -1);
|
|
const int endColNumber = q.getIntField(5, -1);
|
|
const int locationKind = q.getIntField(6, -1);
|
|
|
|
if (id != 0 && fileNodeId != 0 && startLineNumber != -1 && startColNumber != -1 && endLineNumber != -1 && endColNumber != -1 &&
|
|
locationKind != -1)
|
|
{
|
|
sourceLocations.emplace_back(
|
|
StorageSourceLocation(id, fileNodeId, startLineNumber, startColNumber, endLineNumber, endColNumber, locationKind));
|
|
}
|
|
|
|
q.nextRow();
|
|
}
|
|
return sourceLocations;
|
|
}
|
|
|
|
template <>
|
|
std::vector<StorageOccurrence> DatabaseStorage::doGetAll<StorageOccurrence>(const std::string& query) const
|
|
{
|
|
CppSQLite3Query q = executeQuery("SELECT element_id, source_location_id FROM occurrence " + query + ";");
|
|
|
|
std::vector<StorageOccurrence> occurrences;
|
|
|
|
while (!q.eof())
|
|
{
|
|
const int elementId = q.getIntField(0, 0);
|
|
const int sourceLocationId = q.getIntField(1, 0);
|
|
|
|
if (elementId != 0 && sourceLocationId != 0)
|
|
{
|
|
occurrences.emplace_back(StorageOccurrence(elementId, sourceLocationId));
|
|
}
|
|
|
|
q.nextRow();
|
|
}
|
|
return occurrences;
|
|
}
|
|
|
|
template <>
|
|
std::vector<StorageError> DatabaseStorage::doGetAll<StorageError>(const std::string& query) const
|
|
{
|
|
CppSQLite3Query q = executeQuery("SELECT id, message, fatal, indexed, translation_unit FROM error " + query + ";");
|
|
|
|
std::vector<StorageError> errors;
|
|
while (!q.eof())
|
|
{
|
|
const int id = q.getIntField(0, 0);
|
|
const std::string message = q.getStringField(1, "");
|
|
const bool fatal = q.getIntField(2, 0);
|
|
const bool indexed = q.getIntField(3, 0);
|
|
const std::string translationUnit = q.getStringField(4, "");
|
|
|
|
if (id != 0)
|
|
{
|
|
errors.emplace_back(StorageError(id, message, translationUnit, fatal, indexed));
|
|
}
|
|
|
|
q.nextRow();
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
} // namespace sourcetrail
|