#include "tool.hpp" #include #include // Database classes class PreparedStatements { public: sqlite3 *db; sqlite3_stmt *delete_functions_stmt; sqlite3_stmt *delete_imports_stmt; sqlite3_stmt *insert_functions_stmt; sqlite3_stmt *insert_imports_stmt; sqlite3_stmt *delete_globals_stmt; sqlite3_stmt *insert_globals_stmt; sqlite3_stmt *delete_file_functions_stmt; sqlite3_stmt *delete_file_imports_stmt; sqlite3_stmt *delete_file_globals_stmt; void prepareStatement(const char *sql, sqlite3_stmt **stmt, const std::string &error_msg); PreparedStatements(sqlite3 *database) : db(database) { prepareStatement("DELETE FROM Functions WHERE filepath = ?", &delete_functions_stmt, "Failed to prepare delete functions statement"); prepareStatement("DELETE FROM Imports WHERE filepath = ?", &delete_imports_stmt, "Failed to prepare delete imports statement"); prepareStatement("INSERT OR REPLACE INTO Functions (filepath, name, " "address, type, calling_convention, parameter_names, parameter_types, return_type) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", &insert_functions_stmt, "Failed to prepare insert functions statement"); prepareStatement("INSERT OR REPLACE INTO Imports (filepath, name, address, " "type, calling_convention, parameter_names, parameter_types, return_type) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", &insert_imports_stmt, "Failed to prepare insert imports statement"); prepareStatement("DELETE FROM Globals WHERE filepath = ?", &delete_globals_stmt, "Failed to prepare delete globals statement"); prepareStatement("INSERT OR REPLACE INTO Globals (filepath, name, address) " "VALUES (?, ?, ?)", &insert_globals_stmt, "Failed to prepare insert globals statement"); prepareStatement("DELETE FROM Functions WHERE filepath = ?", &delete_file_functions_stmt, "Failed to prepare delete file functions statement"); prepareStatement("DELETE FROM Imports WHERE filepath = ?", &delete_file_imports_stmt, "Failed to prepare delete file imports statement"); prepareStatement("DELETE FROM Globals WHERE filepath = ?", &delete_file_globals_stmt, "Failed to prepare delete file globals statement"); } ~PreparedStatements() { sqlite3_finalize(delete_functions_stmt); sqlite3_finalize(delete_imports_stmt); sqlite3_finalize(insert_functions_stmt); sqlite3_finalize(insert_imports_stmt); sqlite3_finalize(delete_globals_stmt); sqlite3_finalize(insert_globals_stmt); sqlite3_finalize(delete_file_functions_stmt); sqlite3_finalize(delete_file_imports_stmt); sqlite3_finalize(delete_file_globals_stmt); } }; void PreparedStatements::prepareStatement(const char *sql, sqlite3_stmt **stmt, const std::string &error_msg) { if (sqlite3_prepare_v2(db, sql, -1, stmt, nullptr) != SQLITE_OK) { throw std::runtime_error(error_msg + ": " + sqlite3_errmsg(db)); } } DatabaseManager::DatabaseManager(const std::string &db_path) : db(nullptr) { if (sqlite3_open(db_path.c_str(), &db) != SQLITE_OK) { spdlog::error("Can't open database: {}", sqlite3_errmsg(db)); sqlite3_close(db); throw std::runtime_error("Failed to open database"); } const char *create_tables = R"( CREATE TABLE IF NOT EXISTS Functions (filepath TEXT, name TEXT, address TEXT, type INTEGER DEFAULT 0, calling_convention INTEGER DEFAULT 0, parameter_names TEXT DEFAULT '', parameter_types TEXT DEFAULT '', return_type TEXT DEFAULT '', PRIMARY KEY (name, filepath)); CREATE TABLE IF NOT EXISTS Imports (filepath TEXT, name TEXT, address TEXT, type INTEGER DEFAULT 0, calling_convention INTEGER DEFAULT 0, parameter_names TEXT DEFAULT '', parameter_types TEXT DEFAULT '', return_type TEXT DEFAULT '', PRIMARY KEY (name, filepath)); CREATE TABLE IF NOT EXISTS Globals (filepath TEXT, name TEXT, address TEXT); )"; sqlite3_exec(db, create_tables, nullptr, nullptr, nullptr); prepared_stmts = std::make_shared(db); } DatabaseManager::~DatabaseManager() { if (db) sqlite3_close(db); } void DatabaseManager::clearEntriesForFile(const std::string &filepath) { for (auto stmt : {prepared_stmts->delete_functions_stmt, prepared_stmts->delete_imports_stmt}) { sqlite3_reset(stmt); sqlite3_bind_text(stmt, 1, filepath.c_str(), -1, SQLITE_STATIC); sqlite3_step(stmt); } } void DatabaseManager::clearGlobalsForFile(const std::string &filepath) { sqlite3_reset(prepared_stmts->delete_globals_stmt); sqlite3_bind_text(prepared_stmts->delete_globals_stmt, 1, filepath.c_str(), -1, SQLITE_STATIC); sqlite3_step(prepared_stmts->delete_globals_stmt); } void DatabaseManager::insertFunction(const FunctionInfo &func) { sqlite3_stmt *stmt = func.is_import ? prepared_stmts->insert_imports_stmt : prepared_stmts->insert_functions_stmt; sqlite3_reset(stmt); sqlite3_bind_text(stmt, 1, func.filepath.c_str(), -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, func.name.c_str(), -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 3, func.address.c_str(), -1, SQLITE_STATIC); sqlite3_bind_int(stmt, 4, static_cast(func.type)); sqlite3_bind_int(stmt, 5, static_cast(func.calling_convention)); sqlite3_bind_text(stmt, 6, func.parameter_names.c_str(), -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 7, func.parameter_types.c_str(), -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 8, func.return_type.c_str(), -1, SQLITE_STATIC); sqlite3_step(stmt); } void DatabaseManager::insertGlobal(const GlobalInfo &global) { sqlite3_reset(prepared_stmts->insert_globals_stmt); sqlite3_bind_text(prepared_stmts->insert_globals_stmt, 1, global.filepath.c_str(), -1, SQLITE_STATIC); sqlite3_bind_text(prepared_stmts->insert_globals_stmt, 2, global.name.c_str(), -1, SQLITE_STATIC); sqlite3_bind_text(prepared_stmts->insert_globals_stmt, 3, global.address.c_str(), -1, SQLITE_STATIC); sqlite3_step(prepared_stmts->insert_globals_stmt); } void DatabaseManager::beginTransaction() { sqlite3_exec(db, "BEGIN TRANSACTION", nullptr, nullptr, nullptr); } void DatabaseManager::commitTransaction() { sqlite3_exec(db, "COMMIT", nullptr, nullptr, nullptr); } void DatabaseManager::rollbackTransaction() { sqlite3_exec(db, "ROLLBACK", nullptr, nullptr, nullptr); } bool DatabaseManager::checkDuplicateAddresses() { const char *sql = R"( WITH all_addresses AS ( SELECT 'Functions' as table_name, name, address, filepath FROM Functions WHERE address != '' AND type != 3 UNION ALL SELECT 'Globals' as table_name, name, address, filepath FROM Globals WHERE address != '' ) SELECT address, COUNT(*) as count, GROUP_CONCAT(table_name || ':' || name || ' (' || filepath || ')', '; ') as entries FROM all_addresses GROUP BY address HAVING COUNT(*) > 1 ORDER BY address; )"; sqlite3_stmt *stmt; if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) { spdlog::error("Failed to prepare duplicate address query: {}", sqlite3_errmsg(db)); return false; } bool found_duplicates = false; while (sqlite3_step(stmt) == SQLITE_ROW) { found_duplicates = true; const char *address = (const char *)sqlite3_column_text(stmt, 0); int count = sqlite3_column_int(stmt, 1); const char *entries = (const char *)sqlite3_column_text(stmt, 2); spdlog::error("DUPLICATE ADDRESS: {} appears {} times in: {}", address, count, entries); } sqlite3_finalize(stmt); return found_duplicates; } bool DatabaseManager::checkDuplicateNames() { bool found_duplicates = false; // Check Functions table const char *functions_sql = R"( SELECT name, COUNT(*) as count, GROUP_CONCAT(filepath, '; ') as filepaths FROM Functions WHERE type != 3 GROUP BY name HAVING COUNT(*) > 1 ORDER BY name; )"; sqlite3_stmt *stmt; if (sqlite3_prepare_v2(db, functions_sql, -1, &stmt, nullptr) == SQLITE_OK) { while (sqlite3_step(stmt) == SQLITE_ROW) { found_duplicates = true; const char *name = (const char *)sqlite3_column_text(stmt, 0); int count = sqlite3_column_int(stmt, 1); const char *filepaths = (const char *)sqlite3_column_text(stmt, 2); spdlog::error( "DUPLICATE FUNCTION NAME: '{}' appears {} times in files: {}", name, count, filepaths); } sqlite3_finalize(stmt); } // Check Globals table const char *globals_sql = R"( SELECT name, COUNT(*) as count, GROUP_CONCAT(filepath, '; ') as filepaths FROM Globals GROUP BY name HAVING COUNT(*) > 1 ORDER BY name; )"; if (sqlite3_prepare_v2(db, globals_sql, -1, &stmt, nullptr) == SQLITE_OK) { while (sqlite3_step(stmt) == SQLITE_ROW) { found_duplicates = true; const char *name = (const char *)sqlite3_column_text(stmt, 0); int count = sqlite3_column_int(stmt, 1); const char *filepaths = (const char *)sqlite3_column_text(stmt, 2); spdlog::error("DUPLICATE GLOBAL NAME: '{}' appears {} times in files: {}", name, count, filepaths); } sqlite3_finalize(stmt); } return found_duplicates; } std::vector DatabaseManager::getFunctionsByType(FileType type) { std::vector functions; const char *sql = R"( SELECT name, address, filepath, calling_convention, parameter_names, parameter_types, return_type FROM Functions WHERE type = ? AND address != '' ORDER BY address; )"; sqlite3_stmt *stmt; if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) { spdlog::error("Failed to prepare getFunctionsByType query: {}", sqlite3_errmsg(db)); return functions; } sqlite3_bind_int(stmt, 1, static_cast(type)); while (sqlite3_step(stmt) == SQLITE_ROW) { FunctionInfo func; func.name = (const char *)sqlite3_column_text(stmt, 0); func.address = (const char *)sqlite3_column_text(stmt, 1); func.filepath = (const char *)sqlite3_column_text(stmt, 2); func.calling_convention = static_cast(sqlite3_column_int(stmt, 3)); func.parameter_names = (const char *)sqlite3_column_text(stmt, 4); func.parameter_types = (const char *)sqlite3_column_text(stmt, 5); func.return_type = (const char *)sqlite3_column_text(stmt, 6); func.type = type; func.is_import = false; // Functions table contains non-imports functions.push_back(func); } sqlite3_finalize(stmt); return functions; } std::vector DatabaseManager::getAllFiles() { std::vector files; const char *sql = R"( SELECT DISTINCT filepath FROM ( SELECT filepath FROM Functions UNION SELECT filepath FROM Imports UNION SELECT filepath FROM Globals ) ORDER BY filepath; )"; sqlite3_stmt *stmt; if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) { spdlog::error("Failed to prepare getAllFiles query: {}", sqlite3_errmsg(db)); return files; } while (sqlite3_step(stmt) == SQLITE_ROW) { const char *filepath = (const char *)sqlite3_column_text(stmt, 0); if (filepath) { files.push_back(std::string(filepath)); } } sqlite3_finalize(stmt); return files; } void DatabaseManager::removeFile(const std::string &filepath) { // Use prepared statements for efficient deletion sqlite3_stmt *stmts[] = { prepared_stmts->delete_file_functions_stmt, prepared_stmts->delete_file_imports_stmt, prepared_stmts->delete_file_globals_stmt }; for (auto stmt : stmts) { sqlite3_reset(stmt); sqlite3_bind_text(stmt, 1, filepath.c_str(), -1, SQLITE_STATIC); sqlite3_step(stmt); } spdlog::debug("Removed all entries for file: {}", filepath); }