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
Diesel
ein 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.
SQLx
ist 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_table
Dieser Befehl generiert eine neue Migrationsdatei in Ihrem angegebenen Migrationsverzeichnis (normalerweise
migrations/
) mit den Suffixen_up.sql
und_down.sql
zum 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 run
Dies 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 setup
Dieser Befehl richtet die notwendigen Ordner und die
diesel.toml
-Konfigurationsdatei ein. -
Generierung von
schema.rs
:Hier glänzt
diesel-cli
für die Offline-Arbeit.diesel-cli
kann eine Live-Datenbankverbindung inspizieren, um eine Dateisrc/schema.rs
zu generieren, die Rust-Repräsentationen Ihrer Datenbanktabellen und -spalten enthält.diesel print-schema > src/schema.rs
Diese Datei
schema.rs
dient 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-cli
ergibt sich aus seinem Migrationssystem und der Tatsache, dass, sobaldschema.rs
generiert wurde, Ihre Anwendung anhand dieser Datei typsicher geprüft wird, nicht anhand einer Live-Datenbank. Sie können manuell sicherstellen, dassschema.rs
mit Ihren Migrationen synchron bleibt. -
Erstellung einer neuen Migration:
diesel migration generate create_posts_table
Ähnlich wie bei
sqlx-cli
werden hierup.sql
unddown.sql
Dateien 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 run
Dies 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.