Offline-Schema-Management: Nutzung von sqlx-cli und diesel-cli für robuste Rust-Anwendungen
Wenhao Wang
Dev Intern · Leapcell

Einleitung
In der Welt der modernen Softwareentwicklung sind Datenbanken das Fundament fast jeder Anwendung. Mit der Weiterentwicklung von Anwendungen entwickeln sich auch ihre Datenstrukturen weiter. Die Verwaltung dieser Änderungen, bekannt als Datenbankmigrationen, ist eine kritische Aufgabe, die sich direkt auf die Stabilität, Wartbarkeit und Bereitstellungbarkeit von Anwendungen auswirkt. Im Rust-Ökosystem, wo starke Typisierung und Compile-Time-Garantien geschätzt werden, ist es von größter Bedeutung, sicherzustellen, dass die Datenmodelle Ihrer Anwendung mit Ihrem Datenbankschema synchron bleiben. Während viele Werkzeuge zur Verwaltung von Migrationen existieren, bietet die Möglichkeit, Schema-Überprüfungen offline durchzuführen und Migrationen ohne eine Live-Datenbankverbindung zu verwalten, erhebliche Vorteile, insbesondere in CI/CD-Pipelines oder lokalen Entwicklungsumgebungen mit strengen Netzwerkanforderungen. Dieser Artikel befasst sich damit, wie Rust-Entwickler leistungsstarke Kommandozeilen-Tools wie sqlx-cli und diesel-cli nutzen können, um Datenbankmigrationen und -schemata effektiv zu verwalten, mit besonderem Fokus auf ihre unschätzbaren Offline-Fähigkeiten.
Die Landschaft verstehen
Bevor wir uns mit den Besonderheiten von sqlx-cli und diesel-cli befassen, lassen Sie uns einige Kernkonzepte klären, die das Datenbankschema-Management in Rust untermauern:
- Datenbankmigrationen: Dies sind programmatische Änderungen an einem Datenbankschema. Es handelt sich typischerweise um versionierte Dateien (z. B. SQL-Skripte oder Rust-Code), die definieren, wie die Datenbank von einem Zustand in einen anderen überführt wird (z. B. ein neues Tabellenschema hinzufügen, eine Spalte ändern, einen Index erstellen).
- Schema: Die formale Beschreibung aller Tabellen, Spalten, Beziehungen, Indizes und anderer Elemente einer Datenbank.
- ORM (Object-Relational Mapper): Eine Programmierungstechnik, die Daten zwischen inkompatiblen Typsystemen mithilfe objektorientierter Programmiersprachen konvertiert. In Rust ist
Dieselein bekanntes ORM. - Query Builder: Eine Bibliothek, die es Entwicklern ermöglicht, SQL-Abfragen programmatisch zu erstellen, oft unter Bereitstellung von Typsicherheit und Vermeidung von rohen SQL-Strings.
SQLxist primär ein Query Builder und ein asynchroner Datenbanktreiber. - Offline-Schema-Prüfung: Die Fähigkeit, Abweichungen zwischen dem Datenmodell Ihrer Anwendung (z. B. Rust-Strukturen) und Ihrem Datenbankschema zu überprüfen oder Migrationsdateien zu generieren, ohne eine Live-Datenbankverbindung zu benötigen. Dies beschleunigt die Entwicklung erheblich und verbessert die Effizienz von CI/CD.
Sowohl SQLx als auch Diesel bieten robuste Lösungen für die Interaktion mit Datenbanken in Rust, und ihre jeweiligen CLI-Tools erweitern diese Funktionalität auf das Schema-Management. Während Diesel ein ORM ist, das sich auf typsichere Abfragen und Schema-Definitionen im Rust-Code konzentriert, betont SQLx typsichere rohe SQL-Abfragen und einen weniger eingreifenden Ansatz zur Schema-Definition. Beide bieten jedoch leistungsstarke Migrationsdienstprogramme, die für die moderne Entwicklung unerlässlich sind.
sqlx-cli: Typsichere SQL und Offline-Prüfungen
sqlx-cli ist die Kommandozeilenschnittstelle für die SQLx-Datenbankkiste. SQLx ist bekannt für seine Compile-Time-Prüfungen von rohen SQL-Abfragen, die sicherstellen, dass Ihre Abfragen syntaktisch korrekt sind und mit Ihrem Datenbankschema übereinstimmen, bevor Sie Ihre Anwendung ausführen. Hier glänzt sqlx-cli, insbesondere mit seinen Offline-Funktionen sqlx database diff und sqlx migrate add.
Im Kern verwaltet sqlx-cli Migrationen über SQL-Skriptdateien. Ein typischer Migrationsworkflow mit sqlx-cli beinhaltet:
-
Erstellung einer neuen Migration:
sqlx migrate add create_users_tableDieser Befehl generiert eine neue Migrationsdatei in Ihrem angegebenen Migrationsverzeichnis (normalerweise
migrations/) mit den Suffixen_up.sqlund_down.sqlzum Anwenden und Rückgängigmachen der Migration bzw. -
Schreiben von Migrations-SQL: Sie füllen dann diese Dateien mit Ihren SQL DDL-Anweisungen.
-- migrations/20231027100000_create_users_table.up.sql CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), username VARCHAR(255) NOT NULL UNIQUE, email VARCHAR(255) NOT NULL UNIQUE, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP );-- migrations/20231027100000_create_users_table.down.sql DROP TABLE users; -
Anwenden von Migrationen: Typischerweise würden Sie Folgendes ausführen:
sqlx migrate runDies wendet alle ausstehenden Migrationen auf Ihre Datenbank an.
Das Killer-Feature von sqlx-cli im Kontext von Offline-Prüfungen ist seine Fähigkeit, Ihre Anwendungsabfragen anhand Ihrer Migrationshistorie zu verifizieren. SQLx speichert .sqlx-Metadatendateien, die durch frühere Datenbankinteraktionen oder durch sqlx prepare generiert werden. Diese Dateien erfassen das erwartete Schema und die erwarteten Abfragetypen. Wenn Sie neue Abfragen oder ein geändertes Schema haben, kann das Ausführen von sqlx prepare --check die Datenbankmigrationen anhand Ihres Rust-Codes überprüfen und Abweichungen erkennen, ohne eine Live-Datenbankverbindung, wenn Sie eine SQLx DATABASE_URL Umgebungsvariable haben, die auf eine Schema-Datei anstelle einer Live-Datenbank verweist.
Alternativ bietet sqlx-cli selbst leistungsstarke Offline-Prüfungen, ohne dass eine DATABASE_URL zu einer Schema-Datei erforderlich ist. Betrachten Sie ein Projekt, in dem Sie eine rust-Anwendung und eine Reihe von Migrationen haben. Anstatt für jeden CI-Lauf eine Datenbank hochzufahren, nur um zu überprüfen, ob die Abfragen Ihrer Anwendung gegen das erwartete Schema noch gültig sind, kann sqlx-cli das Datenbankschema aus Ihren Migrationsdateien synthetisieren.
# Dieser Befehl kann offline ausgeführt werden, um zu überprüfen, ob Ihre Migrationen gültiges SQL sind # und ob sie offensichtliche Syntaxfehler enthalten. sqlx migrate info
Obwohl sich sqlx-cli primär auf die Ausführung von Migrationen konzentriert, ist der Befehl sqlx database diff ein leistungsstarkes experimentelles Feature, das darauf abzielt, die Offline-Schema-Generierung und -Vergleich direkt aus Ihren Rust-Strukturen heraus zu behandeln oder zwei verschiedene Datenbankzustände zu vergleichen, die durch Schemata-Dateien oder eine Live-Datenbank dargestellt werden. Obwohl es noch nicht vollständig ausgereift für die Offline-Schema-Generierung aus Rust-Strukturen ist, zeigt seine Entwicklung einen klaren Weg zu einem ausgefeilteren Offline-Schema-Management. Vorerst kommt die primäre "Offline-Prüfung" für sqlx-Benutzer oft von den Compile-Time-Prüfungen, die durch die .sqlx-Metadatendateien ermöglicht werden, welche implizit durch cargo check überprüft werden, solange SQLX_OFFLINE auf true gesetzt ist (oder durch sqlx prepare --check).
// src/main.rs #[macro_use] extern crate sqlx; #[tokio::main] async fn main() -> Result<(), sqlx::Error> { // In einer echten Anwendung würde DATABASE_URL aus Umgebungsvariablen geladen werden let database_url = std::env::var("DATABASE_URL") .expect("DATABASE_URL must be set"); let pool = sqlx::PgPool::connect(&database_url).await?; let row: (i64,) = sqlx::query_as("SELECT $1 FROM users") // Fehler hier, wenn die Tabelle `users` nicht existiert .bind(150_i64) .fetch_one(&pool) .await?; println!("{}", row.0); Ok(()) }
Wenn SQLX_OFFLINE=true gesetzt ist, verwendet sqlx zwischengespeicherte Informationen, um Abfragen zu validieren, wodurch cargo check effektiv zu einem Offline-Schema-Validierungswerkzeug für Ihre Abfragen wird. Dies ist ein unglaublich leistungsstarkes Feature für CI/CD.
diesel-cli: ORM-gesteuerte Migrationen und Schema-Generierung
diesel-cli ist das Kommandozeilen-Tool für das Diesel-ORM. Diesel verfolgt einen anderen Ansatz für Datenbankinteraktionen, wobei der Schwerpunkt auf der Definition Ihres Schemas direkt im Rust-Code liegt (mittels Makros). Dies ermöglicht es, einen hohen Grad an Typsicherheit für Abfragen und Manipulationen zu bieten.
Das Herzstück des Schema-Managements von diesel-cli ist die Datei schema.rs und das Migrationssystem.
-
Initialisierung von Diesel:
diesel setupDieser Befehl richtet die notwendigen Ordner und die
diesel.toml-Konfigurationsdatei ein. -
Generierung von
schema.rs:Hier glänzt
diesel-clifür die Offline-Arbeit.diesel-clikann eine Live-Datenbankverbindung inspizieren, um eine Dateisrc/schema.rszu generieren, die Rust-Repräsentationen Ihrer Datenbanktabellen und -spalten enthält.diesel print-schema > src/schema.rsDiese Datei
schema.rsdient als einzige Quelle der Wahrheit für Ihr Datenbankschema innerhalb Ihrer Rust-Anwendung. Allerdings benötigt dieser spezifische Befehl eine Live-Datenbankverbindung.Die Offline-Leistung von
diesel-cliergibt sich aus seinem Migrationssystem und der Tatsache, dass, sobaldschema.rsgeneriert wurde, Ihre Anwendung anhand dieser Datei typsicher geprüft wird, nicht anhand einer Live-Datenbank. Sie können manuell sicherstellen, dassschema.rsmit Ihren Migrationen synchron bleibt. -
Erstellung einer neuen Migration:
diesel migration generate create_posts_tableÄhnlich wie bei
sqlx-cliwerden hierup.sqlunddown.sqlDateien für Ihre Migration erstellt. -
Schreiben von Migrations-SQL:
-- migrations/20231027100000_create_posts_table/up.sql CREATE TABLE posts ( id SERIAL PRIMARY KEY, title VARCHAR NOT NULL, body TEXT NOT NULL, published BOOLEAN NOT NULL DEFAULT FALSE );-- migrations/20231027100000_create_posts_table/down.sql DROP TABLE posts; -
Anwenden von Migrationen:
diesel migration runDies wendet die Migrationen auf Ihre Datenbank an.
Der Schlüsselanwendungsfall für Offline-Prüfungen mit diesel-cli ergibt sich, wenn Sie sicherstellen müssen, dass Ihre src/schema.rs den Zustand Ihrer Migrationen korrekt widerspiegelt, ohne eine Live-Datenbankverbindung herzustellen. Während diesel print-schema eine Verbindung benötigt, können Sie diesel migration run mit einer temporären In-Memory-Datenbank (falls für Ihr gewähltes Backend verfügbar, z. B. SQLite) oder einer Docker-basierten Testdatenbank, die in Ihrer CI hoch- und heruntergefahren wird, integrieren. Für PostgreSQL oder MySQL würde dies typischerweise die Einrichtung einer Testdatenbank erfordern.
Die wahre Offline-Leistung ergibt sich jedoch aus der Tatsache, dass die Typsicherheit Ihres Rust-Codes durch schema.rs garantiert wird. Sie könnten schema.rs einmal generieren, und dann einfach Ihre Anwendung kompilieren. Der Rust-Compiler fängt dann jede Diskrepanz ab, falls Ihr schema.rs nicht mehr mit dem durch Ihre Rust-Strukturen repräsentierten Schema übereinstimmt.
Betrachten Sie eine einfache Diesel-Anwendung:
// src/schema.rs (generiert von diesel print-schema oder manuell gemäß Migrationen gepflegt) diesel::table! { posts (id) { id -> Int4, title -> Varchar, body -> Text, published -> Bool, } } // src/models.rs use crate::schema::posts; use diesel::Queryable; #[derive(Queryable)] pub struct Post { pub id: i32, pub title: String, pub body: String, pub published: bool, } // src/main.rs use diesel::prelude::*; use diesel::pg::PgConnection; fn main() { let database_url = std::env::var("DATABASE_URL") .expect("DATABASE_URL must be set"); let mut connection = PgConnection::establish(&database_url) .expect("Error connecting to database"); // Diese Abfrage wird gegen `src/schema.rs` typsicher geprüft let results: Vec<crate::models::Post> = posts::table .filter(posts::published.eq(true)) .limit(5) .load(&mut connection) .expect("Error loading posts"); println!("Found {} published posts.", results.len()); }
Wenn Sie Ihre Migrationen ändern (z. B. eine neue Spalte zu posts hinzufügen) und Sie src/schema.rs nicht aktualisieren, wird Ihr cargo build oder cargo check wahrscheinlich fehlschlagen, wenn Ihr Rust-Code sich auf das alte schema.rs stützt. Sie können dann die Schemaänderungen neu bewerten, indem Sie Migrationen auf einer temporären Datenbank ausführen und src/schema.rs neu generieren. Dieser Prozess hilft, die Konsistenz ohne ständige Verfügbarkeit einer Live-Produktionsdatenbankinstanz sicherzustellen.
Für CI/CD kann der Befehl diesel migration check die Datei schema.rs mit dem vergleichen, was generiert würde, wenn alle Migrationen ausgeführt würden, und Abweichungen melden:
# Dieser Befehl *benötigt* eine Datenbankverbindung, um die Migrationen auszuführen # und dann das Schema zu vergleichen, könnte aber auf eine In-Memory- oder ephemere DB verweisen. diesel migration check
Die wahre Offline-Prüfung kommt von cargo-check nachdem schema.rs generiert wurde und das gewünschte Schema widerspiegelt. Jede Abweichung davon, wie Ihre Rust-Strukturen mit der Datenbank interagieren, wie von schema.rs beschrieben, führt zu Kompilierungsfehlern.
Fazit
Sowohl sqlx-cli als auch diesel-cli bieten robuste Lösungen für die Verwaltung von Datenbankmigrationen und -schemata in Rust. Während sqlx-cli bei der Compile-Time-Prüfung von rohen SQL-Abfragen gegen ein Schema, das aus einer bestehenden Datenbank oder seinen eigenen Metadaten abgeleitet wird, hervorragend geeignet ist und eine leistungsstarke "Offline"-Abfragevalidierung für SQLx-Benutzer bietet, nutzt diesel-cli seinen ORM-Ansatz, um eine schema.rs-Datei zu generieren, gegen die Ihr Rust-Code typsicher geprüft wird. Die Fähigkeit, Ihr Schema und Ihre Migrationen mit diesen Tools zu verwalten und zu überprüfen, selbst in Szenarien, in denen eine Live-Datenbankverbindung nicht persistent verfügbar ist, optimiert die Entwicklungs-Workflows erheblich, stärkt CI/CD-Pipelines und führt letztendlich zu zuverlässigeren Rust-Anwendungen. Die Beherrschung dieser CLI-Tools ist für jeden Rust-Entwickler, der datenbankgestützte Anwendungen erstellt, unerlässlich.