C++ में डेटाबेस माइग्रेशन: स्कीमा प्रबंधन और वर्ज़निंग
आधुनिक C++ प्रोजेक्ट्स में जो रिलेशनल डेटाबेस (PostgreSQL, MySQL, SQLite) के साथ काम करते हैं, स्कीमा के विकास का समर्थन करना एक अनिवार्य अभ्यास है। माइग्रेशन तालिकाओं, इंडेक्स और बाधाओं की संरचना में परिवर्तनों को स्वचालित रूप से लागू करने की अनुमति देते हैं, जिससे एप्लिकेशन के विभिन्न संस्करणों के बीच डेटा अखंडता बनी रहती है। डायनामिक भाषाओं के विपरीत, C++ में माइग्रेशन के लिए कोई अंतर्निहित उपकरण नहीं है, लेकिन इस कार्य को हल करने वाले कई परिपक्व पुस्तकालय मौजूद हैं।
C++ के लिए माइग्रेशन पुस्तकालय की मुख्य आवश्यकताएँ
- वर्ज़निंग: प्रत्येक माइग्रेशन में एक अद्वितीय पहचानकर्ता (संख्यात्मक या टाइमस्टैम्प) होना चाहिए।
- रोलबैक समर्थन: "up" (निर्माण/परिवर्तन) और "down" (हटाना/वापस लेना) संचालन लागू करने की क्षमता।
- DBMS के साथ एकीकरण: मानक ड्राइवरों (libpq, sqlite3, MySQL Connector) के माध्यम से कार्य करना।
- लेन-देन क्षमता: परमाणुता के लिए प्रत्येक माइग्रेशन एक अलग लेन-देन में निष्पादित होता है।
- स्वचालित आवेदन: एप्लिकेशन प्रारंभ होने पर चलाने के लिए कमांड-लाइन उपयोगिता या API।
C++ पर माइग्रेशन के लिए लोकप्रिय उपकरण
1. cpp-migrations (अनुशंसित)
एक हल्का ओपन-सोर्स पुस्तकालय जो PostgreSQL, MySQL, SQLite का समर्थन करता है। माइग्रेशन को up() और down() विधियों वाले C++ क्लास के रूप में वर्णित किया गया है। संस्करण migrations.json फ़ाइल में तय किया गया है।
// 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");API के माध्यम से माइग्रेशन चलाना:
#include <migration_runner.hpp>
int main() { MigrationRunner runner("postgresql://user:pass@localhost/db"); runner.applyPending(); // सभी अनलागू माइग्रेशन लागू करता है return 0;}2. libpq-आधारित माइग्रेशन (स्व-निर्मित दृष्टिकोण)
यदि प्रोजेक्ट पहले से ही libpq (PostgreSQL का मूल क्लाइंट) का उपयोग कर रहा है, तो SQL फ़ाइलों के आधार पर एक सरल माइग्रेशन प्रबंधक लागू किया जा सकता है। संरचना का उदाहरण:
migrations/├── 001_create_users.sql├── 001_create_users.down.sql├── 002_add_role_column.sql└── 002_add_role_column.down.sqlC++ में लोडर कोड:
#include <pqxx/pqxx>#include <filesystem>#include <fstream>
void applyMigrations(pqxx::connection &conn) { pqxx::work txn(conn); // लागू माइग्रेशन को ट्रैक करने के लिए तालिका बनाएँ 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; // जाँचें कि माइग्रेशन लागू हुआ है या नहीं 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