diff --git a/native/cpp/CommonCpp/DatabaseManagers/NativeSQLiteConnectionManager.h b/native/cpp/CommonCpp/DatabaseManagers/NativeSQLiteConnectionManager.h --- a/native/cpp/CommonCpp/DatabaseManagers/NativeSQLiteConnectionManager.h +++ b/native/cpp/CommonCpp/DatabaseManagers/NativeSQLiteConnectionManager.h @@ -14,6 +14,8 @@ std::string logID, std::uint8_t *patchsetPtr, int patchsetSize); + std::vector + getAttachmentsFromLog(std::uint8_t *patchsetPtr, int patchsetSize); public: NativeSQLiteConnectionManager(); diff --git a/native/cpp/CommonCpp/DatabaseManagers/NativeSQLiteConnectionManager.cpp b/native/cpp/CommonCpp/DatabaseManagers/NativeSQLiteConnectionManager.cpp --- a/native/cpp/CommonCpp/DatabaseManagers/NativeSQLiteConnectionManager.cpp +++ b/native/cpp/CommonCpp/DatabaseManagers/NativeSQLiteConnectionManager.cpp @@ -9,6 +9,8 @@ namespace comm { +const std::string BLOB_SERVICE_PREFIX = "comm-blob-service://"; + void NativeSQLiteConnectionManager::attachSession() { int sessionCreationResult = sqlite3session_create(dbConnection, "main", &backupLogsSession); @@ -63,6 +65,93 @@ "Failed to rename complete log file from temporary path to target " "path."); } + + std::vector attachments = + getAttachmentsFromLog(patchsetPtr, patchsetSize); + if (!attachments.size()) { + return; + } + + std::string finalAttachmentsPath = + PlatformSpecificTools::getBackupLogFilePath(backupID, logID, true); + std::string tempAttachmentsPath = finalAttachmentsPath + "_tmp"; + + std::ofstream tempAttachmentsFile( + tempAttachmentsPath, std::ofstream::out | std::ofstream::trunc); + + if (!tempAttachmentsFile.is_open()) { + throw std::runtime_error("Failed to open temporary log attachments file."); + } + + for (const auto &attachment : attachments) { + tempAttachmentsFile << attachment << std::endl; + } + tempAttachmentsFile.close(); + + if (std::rename(tempAttachmentsPath.c_str(), finalAttachmentsPath.c_str())) { + throw std::runtime_error( + "Failed to rename complete log attachments file from temporary path to " + "target " + "path."); + } +} + +std::vector NativeSQLiteConnectionManager::getAttachmentsFromLog( + std::uint8_t *patchsetPtr, + int patchsetSize) { + std::vector attachments; + sqlite3_changeset_iter *patchsetIter; + int startIterResult = + sqlite3changeset_start(&patchsetIter, patchsetSize, patchsetPtr); + handleSQLiteError(startIterResult, "Failed to initialize log iterator."); + + int nextResult; + for (nextResult = sqlite3changeset_next(patchsetIter); + nextResult == SQLITE_ROW; + nextResult = sqlite3changeset_next(patchsetIter)) { + const char *tableName; + int columnsNumber; + int operationType; + + int getOperationResult = sqlite3changeset_op( + patchsetIter, &tableName, &columnsNumber, &operationType, nullptr); + handleSQLiteError( + getOperationResult, "Failed to extract operation from log iterator."); + + if (std::string(tableName) != "media") { + continue; + } + + if (operationType != SQLITE_UPDATE && operationType != SQLITE_INSERT) { + continue; + } + + sqlite3_value *uriFromMediaRow; + // In "media" table "uri" column has index 3 (starting from 0) + int getURIResult = sqlite3changeset_new(patchsetIter, 3, &uriFromMediaRow); + handleSQLiteError( + getURIResult, + "Failed to extract uri value of media row from log iterator."); + + if (!uriFromMediaRow) { + continue; + } + + std::string uri = std::string( + reinterpret_cast(sqlite3_value_text(uriFromMediaRow))); + if (uri.compare(0, BLOB_SERVICE_PREFIX.size(), BLOB_SERVICE_PREFIX)) { + continue; + } + attachments.push_back(uri.substr(BLOB_SERVICE_PREFIX.size())); + } + + handleSQLiteError( + nextResult, "Error while iterating over a log.", SQLITE_DONE); + + int finalizeIterResult = sqlite3changeset_finalize(patchsetIter); + handleSQLiteError(finalizeIterResult, "Failed to finalize log iterator."); + + return attachments; } NativeSQLiteConnectionManager::NativeSQLiteConnectionManager()