#include "repositories/MigrationRunner.h" #include #include #include #include #include #include #include #include namespace visione::repositories { namespace { std::string trim(std::string value) { auto notSpace = [](unsigned char ch) { return !std::isspace(ch); }; value.erase(value.begin(), std::find_if(value.begin(), value.end(), notSpace)); value.erase(std::find_if(value.rbegin(), value.rend(), notSpace).base(), value.end()); return value; } std::vector splitStatements(const std::string &sql) { std::vector statements; std::string current; bool inSingleQuote = false; bool inDoubleQuote = false; for (char ch : sql) { if (ch == '\'' && !inDoubleQuote) { inSingleQuote = !inSingleQuote; } else if (ch == '"' && !inSingleQuote) { inDoubleQuote = !inDoubleQuote; } if (ch == ';' && !inSingleQuote && !inDoubleQuote) { auto statement = trim(current); if (!statement.empty()) { statements.push_back(std::move(statement)); } current.clear(); continue; } current.push_back(ch); } auto statement = trim(current); if (!statement.empty()) { statements.push_back(std::move(statement)); } return statements; } } // namespace void MigrationRunner::run(const std::string &directory) { namespace fs = std::filesystem; std::vector files; if (!fs::exists(directory)) { LOG_WARN << "Migration directory not found: " << directory; return; } for (const auto &entry : fs::directory_iterator(directory)) { if (entry.is_regular_file() && entry.path().extension() == ".sql") { files.push_back(entry.path()); } } std::sort(files.begin(), files.end()); auto db = drogon::app().getDbClient(); for (const auto &file : files) { std::ifstream input(file); std::stringstream buffer; buffer << input.rdbuf(); for (const auto &statement : splitStatements(buffer.str())) { db->execSqlSync(statement); } LOG_INFO << "Applied migration " << file.string(); } } } // namespace visione::repositories