Orbit Framework
An ultra-modern, asynchronous, cross-platform C++ Web Framework.
Loading...
Searching...
No Matches
MigrationRunner.hpp
Go to the documentation of this file.
1#pragma once
2#include <string>
3#include <vector>
4#include <filesystem>
5#include <fstream>
6#include <iostream>
7#include <algorithm>
10
11
12
13namespace orm {
14
21template <typename DbClient>
23public:
24 static concurrency::Task run_migrations(std::shared_ptr<DbClient> db, const std::string& migrations_dir, std::shared_ptr<http::ResponseWriter> res) {
25 // 1. Create tracking table
26 co_await database::query_async(db,
27 "CREATE TABLE IF NOT EXISTS orbit_migrations ("
28 "id SERIAL PRIMARY KEY, "
29 "version VARCHAR(255) UNIQUE NOT NULL, "
30 "applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);"
31 );
32
33 // 2. Fetch applied migrations
34 database::ResultSet applied_res = co_await database::query_async(db,
35 "SELECT version FROM orbit_migrations ORDER BY version ASC;"
36 );
37
38 std::vector<std::string> applied_versions;
39 for (size_t i = 0; i < applied_res.size(); ++i) {
40 applied_versions.push_back(applied_res[i].get(0).value_or(""));
41 }
42
43 // 3. Scan directory
44 if (!std::filesystem::exists(migrations_dir)) {
45 std::cout << "[Migrations] Directory '" << migrations_dir << "' not found. Skipping migrations.\n";
46 res->send(http::HttpResponse().status(http::HttpStatus::OK).send("Migrations skipped - no directory"));
47 co_return;
48 }
49
50 std::vector<std::string> pending_files;
51 for (const auto& entry : std::filesystem::directory_iterator(migrations_dir)) {
52 if (entry.path().extension() == ".sql") {
53 pending_files.push_back(entry.path().string());
54 }
55 }
56 std::sort(pending_files.begin(), pending_files.end());
57
58 // 4. Apply pending migrations
59 int executed = 0;
60 for (const auto& filepath : pending_files) {
61 std::string filename = std::filesystem::path(filepath).filename().string();
62
63 if (std::find(applied_versions.begin(), applied_versions.end(), filename) == applied_versions.end()) {
64 std::cout << "[Migrations] Applying " << filename << "..." << std::endl;
65
66 std::ifstream ifs(filepath);
67 if (!ifs.is_open()) {
68 std::cerr << "[Migrations] Failed to open " << filepath << std::endl;
69 continue;
70 }
71
72 std::string sql((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
73
74 // Run the migration SQL
75 co_await database::query_async(db, sql);
76
77 // Record it in the tracking table
78 std::string insert_tracking = "INSERT INTO orbit_migrations (version) VALUES ('" + filename + "');";
79 co_await database::query_async(db, insert_tracking);
80
81 executed++;
82 }
83 }
84
85 if (executed == 0) {
86 res->send(http::HttpResponse().status(http::HttpStatus::OK).send("Database is up to date"));
87 } else {
88 res->send(http::HttpResponse().status(http::HttpStatus::OK).send("Successfully applied " + std::to_string(executed) + " migrations."));
89 }
90 }
91};
92
93} // namespace orm
Represents a generic, unified result set from a database query.
Definition ResultSet.hpp:111
size_t size() const
Definition ResultSet.hpp:118
Represents an HTTP response to be sent to a client.
Definition HttpResponse.hpp:50
Simple Database Migration Runner for C++ Coroutines.
Definition MigrationRunner.hpp:22
static concurrency::Task run_migrations(std::shared_ptr< DbClient > db, const std::string &migrations_dir, std::shared_ptr< http::ResponseWriter > res)
Definition MigrationRunner.hpp:24
QueryAwaiter query_async(std::shared_ptr< PostgresClient > client, const std::string &sql)
Creates an awaiter for executing a query on a PostgresClient.
Definition PostgresCoro.hpp:65
Definition MigrationRunner.hpp:13
Definition Task.hpp:7