Migrações de banco de dados em C++: gerenciamento de esquema e versionamento
Em projetos C++ modernos que trabalham com bancos de dados relacionais (PostgreSQL, MySQL, SQLite), o suporte à evolução do esquema é uma prática obrigatória. As migrações permitem aplicar automaticamente alterações na estrutura de tabelas, índices e restrições, preservando a integridade dos dados entre as versões da aplicação. Ao contrário das linguagens dinâmicas, o C++ não possui uma ferramenta embutida para migrações, mas existem várias bibliotecas maduras que resolvem esse problema.
Requisitos principais para uma biblioteca de migrações para C++
- Versionamento: cada migração deve ter um identificador único (numérico ou carimbo de data/hora).
- Suporte a reversão: capacidade de aplicar operações "up" (criação/alteração) e "down" (remoção/reversão).
- Integração com SGBD: funcionamento através de drivers padrão (libpq, sqlite3, MySQL Connector).
- Transacionalidade: cada migração é executada em uma transação separada para atomicidade.
- Aplicação automática: utilitário de linha de comando ou API para execução na inicialização da aplicação.
Ferramentas populares para migrações em C++
1. cpp-migrations (recomendado)
Biblioteca leve de código aberto, com suporte a PostgreSQL, MySQL, SQLite. As migrações são descritas como classes C++ com os métodos up() e down(). A versão é fixada no arquivo migrations.json.
// Exemplo de migração para cpp-migrations#include <migration.hpp>
class CreateUsersTable : public Migration {public: void up() override { execute("CREATE TABLE users (" "id SERIAL PRIMARY KEY," "name VARCHAR(100) NOT NULL," "email VARCHAR(255) UNIQUE," "created_at TIMESTAMP DEFAULT NOW()" ")"); }
void down() override { execute("DROP TABLE IF EXISTS users"); }};
REGISTER_MIGRATION(CreateUsersTable, "001_create_users");Execução de migrações via API:
#include <migration_runner.hpp>
int main() { MigrationRunner runner("postgresql://user:pass@localhost/db"); runner.applyPending(); // aplica todas as migrações pendentes return 0;}2. Migrações baseadas em libpq (abordagem personalizada)
Se o projeto já utiliza libpq (cliente nativo do PostgreSQL), é possível implementar um gerenciador de migrações simples baseado em arquivos SQL. Exemplo de estrutura:
migrations/├── 001_create_users.sql├── 001_create_users.down.sql├── 002_add_role_column.sql└── 002_add_role_column.down.sqlCódigo do carregador em C++:
#include <pqxx/pqxx>#include <filesystem>#include <fstream>
void applyMigrations(pqxx::connection &conn) { pqxx::work txn(conn); // Cria tabela para rastrear migrações aplicadas txn.exec("CREATE TABLE IF NOT EXISTS _migrations (" "id SERIAL PRIMARY KEY," "name TEXT UNIQUE," "applied_at TIMESTAMP DEFAULT NOW())"); for (const auto &entry : std::filesystem::directory_iterator("migrations")) { std::string filename = entry.path().filename().string(); if (filename.find(".down.") != std::string::npos) continue; if (filename.find(".sql") == std::string::npos) continue; // Verifica se a migração já foi aplicada auto check = txn.exec_params("SELECT 1 FROM _migrations WHERE name = $1", filename); if (check.empty()) { std::ifstream file(entry.path()); std::string sql((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); txn.exec(sql); txn.exec_params("INSERT INTO _migrations (name) VALU Também na biblioteca
pybind11 para C++: guia completo com exemplos práticos
Geração Swagger/OpenAPI para Node.js (JavaScript): ferramentas e exemplos
Frameworks CLI para Rust: visão geral das melhores ferramentas para criar utilitários de linha de comando
Quartz Scheduler para Java: guia completo com exemplos práticos
Ktor Client Kotlin: guia completo com exemplos práticos