10 Punkte von GN⁺ 2025-10-16 | 2 Kommentare | Auf WhatsApp teilen
  • SQLite wird seit den Anfängen (2000) in C entwickelt – aus Gründen der Performance, Kompatibilität, geringen Abhängigkeiten und Stabilität
  • C kann auf nahezu allen Betriebssystemen und aus fast allen Sprachen heraus genutzt werden und unterstützt insbesondere als Low-Level-Bibliothek eine schnelle Ausführung
  • Statt einer objektorientierten Sprache wurde C gewählt – wegen der Erweiterbarkeit, der Aufrufbarkeit aus vielen Sprachen und weil C++ und Java damals noch unreif waren
  • SQLite hat eine Einzeldatei-Struktur mit kaum Abhängigkeiten und verwendet nur ein Minimum an Funktionen aus der C-Standardbibliothek
  • Es gibt Diskussionen über eine Neuschreibung in „sicheren Sprachen“ wie Rust und Go, doch bei Qualitätskontrolle, Performance und Aufrufbarkeit als Bibliothek liegt C weiterhin vorn

1. Warum C die optimale Wahl ist

  • SQLite wird seit der ersten Entwicklung am 29. Mai 2000 bis heute in C gepflegt
    • Derzeit gibt es keine Pläne, es in einer anderen Sprache neu zu schreiben
  • C bietet hardware-nahe Kontrolle und zugleich hohe Portabilität und wird daher oft als „portable Assemblersprache“ bezeichnet
  • Andere Sprachen können behaupten, „so schnell wie C“ zu sein, aber keine behauptet, schneller als C zu sein

1.1. Performance

  • Eine Low-Level-Bibliothek wie SQLite wird häufig aufgerufen und muss daher extrem schnell arbeiten
  • C eignet sich gut zum Schreiben schneller Programme, ist portabel und erlaubt zugleich einen hardwarenahen Zugriff
  • Auch wenn andere moderne Sprachen behaupten, „so schnell wie C“ zu sein, gibt es im allgemeinen Einsatz keine Sprache, die überzeugend als schneller als C gilt
  • C erlaubt eine feine Kontrolle über Speicher- und CPU-Ressourcen und erreicht dadurch teils 35 % bessere Performance als das Dateisystem

1.2. Kompatibilität

  • Fast jedes System kann in C geschriebene Bibliotheken aufrufen
  • Beispielsweise lässt sich SQLite auch unter Android (Java-basiert) über einen Adapter verwenden
  • Wäre SQLite in Java geschrieben worden, könnte es auf dem iPhone (Objective-C, Swift) nicht genutzt werden, was die Universalität stark einschränken würde

1.3. Geringe Abhängigkeiten

  • Weil SQLite als C-Bibliothek entwickelt wurde, hat es nur sehr geringe Laufzeitabhängigkeiten
  • In der Minimal-Konfiguration werden nur sehr grundlegende Funktionen der C-Standardbibliothek verwendet: memcmp(), memcpy(), memmove(), memset(), strcmp(), strlen(), strncmp()
  • Selbst vollständigere Builds haben nur wenige zusätzliche Abhängigkeiten wie malloc(), free() und Datei-I/O
  • Moderne Sprachen erfordern oft große Laufzeitumgebungen und tausende Interfaces

1.4. Stabilität

  • C ist eine alte, langweilige Sprache mit wenig Veränderung, doch genau das bedeutet Vorhersehbarkeit und Stabilität
  • Für eine kleine, schnelle und zuverlässige Datenbank-Engine wie SQLite ist eine Sprache geeignet, deren Spezifikation sich nicht ständig ändert
  • Wenn sich Sprachspezifikation oder Implementierung häufig ändern, wirkt sich das nachteilig auf die Stabilität von SQLite aus

2. Warum nicht in einer objektorientierten Sprache?

  • Manche Entwickler glauben, ein komplexes System wie SQLite lasse sich ohne Objektorientierung schwer umsetzen, aber Bibliotheken in C++ oder Java sind im Vergleich zu C schwerer aus anderen Sprachen aufzurufen
  • Für die Unterstützung vieler Sprachen wie Haskell, Java usw. war die Wahl einer C-Bibliothek sinnvoll
  • Objektorientierung ist keine Sprache, sondern ein Entwurfsmuster und daher nicht auf bestimmte Sprachen beschränkt
    • Auch in C lassen sich mit Strukturen und Funktionszeigern objektorientierte Muster umsetzen
  • Objektorientierung ist nicht immer die beste Struktur; prozeduraler Code kann klarer, leichter wartbar und manchmal auch schneller sein
  • In den frühen Jahren der SQLite-Entwicklung (um 2000) galt:
    • Java war unreif
    • C++ hatte ernste Kompatibilitätsprobleme zwischen Compilern
      → Damals war C die praktischste und sicherste Wahl
  • Auch heute gibt es nur wenige Vorteile, die eine Neuschreibung von SQLite rechtfertigen würden

3. Warum nicht in einer „sicheren Sprache“?

  • In jüngerer Zeit ist das Interesse an sicheren Programmiersprachen wie Rust und Go gestiegen, aber als SQLite ursprünglich entwickelt wurde (in den ersten zehn Jahren), existierten diese Sprachen noch nicht
  • Eine Neuschreibung in Go oder Rust könnte mehr Bugs verursachen oder die Performance verschlechtern
  • Diese Sprachen fügen zusätzlichen Verzweigungscode für Speicherprüfungen usw. ein; in der Qualitätsstrategie von SQLite ist jedoch 100% Branch Coverage entscheidend, und dieser Punkt ist nicht erfüllt
  • Sichere Sprachen beenden Programme in Out-of-Memory-Situationen meist, SQLite ist dagegen so ausgelegt, dass es sich auch bei Speichermangel erholen kann
  • Rust, Go usw. sind weiterhin junge Sprachen und benötigen fortlaufende Weiterentwicklung
  • Deshalb begrüßt das SQLite-Entwicklungsteam zwar die Weiterentwicklung sicherer Sprachen, legt für die SQLite-Implementierung aber weiterhin Wert auf die bewährte Stabilität von C

Trotzdem besteht irgendwann die Möglichkeit einer Neuschreibung in Rust. In Go ist das eher unwahrscheinlich, weil Go assert() nicht mag

  • Für eine Umsetzung in Rust gäbe es jedoch Voraussetzungen:
    • Rust müsste reifer werden und sich langsamer verändern, also zu einer „alten und langweiligen Sprache“ werden
    • Es müsste bewiesen sein, dass sich damit eine universell aus vielen Sprachen aufrufbare Bibliothek erstellen lässt
    • Es müsste Objektcode erzeugt werden können, der auch auf Geräten ohne Betriebssystem, etwa in Embedded-Systemen, läuft
    • Es müssten Werkzeuge für 100% Branch-Coverage-Tests auf kompilierten Binärdateien verfügbar sein
    • OOM-Fehler (Speichermangel) müssten recoverbar sein
    • Rust müsste alles, was C in SQLite leistet, ohne Performanceverlust erledigen können
  • Falls ein Rust-Fan (rustacean) meint, dass all diese Bedingungen bereits erfüllt sind und SQLite in Rust neu geschrieben werden sollte, wird empfohlen, die SQLite-Entwickler direkt zu kontaktieren und dafür zu argumentieren

2 Kommentare

 
GN⁺ 2025-10-16
Hacker-News-Kommentare
  • Auch wenn es in den ersten zehn Jahren von SQLite noch keine sicheren Programmiersprachen gab, würde eine Neuimplementierung von SQLite in Go oder Rust vermutlich eher mehr Bugs erzeugen, als sie beheben würde, und könnte zudem langsamer sein. Wenn nach enorm viel Zeit und Tests bereits fehlerfreier Code entstanden ist, spielt es bei einer niedrigen Änderungsrate keine große Rolle mehr, in welcher Sprache er geschrieben ist. Sogar Assembler wäre dann noch akzeptabel
    • Dass „wenig Änderungen zu weniger Problemen führen“, wurde auch im Google Security Blog erläutert. Dort wird argumentiert, dass die meisten Memory-Safety-Probleme in neuem Code entstehen und Code mit der Zeit sicherer wird passender Link
    • Im Rust-Umfeld gibt es mit Projekten wie Turso durchaus aktive Bewegung Turso
    • Manche vertreten auch die Ansicht, dass man die Basis-Utilities von Linux nicht in Rust neu schreiben sollte. Software, die seit Jahrzehnten genutzt wird und bei der die meisten Bugs bereits entfernt wurden, muss nicht zwangsläufig neu geschrieben werden
    • Zig halte ich für gut geeignet, um Teile von C-Code zu ersetzen. Es arbeitet auch gut mit Python und bestehenden C-Binärdateien zusammen. Die Philosophie von Go gefällt mir zwar, aber Optimierung war schwierig und erforderte sehr gute Entwickler. Rust wäre ebenfalls möglich, aber es war deutlich einfacher, bestehenden C-Code weiter zu nutzen und Zig schrittweise einzuführen. Wir konnten Bugs im C-Code nicht vollständig beseitigen, aber ein Wechsel zu Rust wirkte praktisch nur schwer umsetzbar
    • Es gibt bereits eine nach Go portierte SQLite-Implementierung cznic/sqlite
  • Neben den Gründen, warum C zur Entstehungszeit von SQLite die beste Wahl war, und den heutigen Vorteilen sehe ich keinen besonderen Grund, SQLite unbedingt in einer anderen Sprache neu zu schreiben. Eine leichtgewichtige SQL-Datenbank kann grundsätzlich jeder implementieren, also kann man in Rust, C++, Go, Lisp oder jeder anderen gewünschten Sprache eine neue Implementierung bauen. Es gibt keinen Anlass, die bestehende, in C gut funktionierende Implementierung unnötig aufzugeben und Entwickler, die SQLite seit über 25 Jahren in C pflegen, dazu zu drängen, eine neue Sprache zu lernen und alles von Grund auf neu zu bauen
    • In vielen Sprach-Fangemeinden gibt es die Tendenz, anderen das aufzudrängen, was man selbst möchte, und die Wahl einer Sprache ist stellenweise zu einem Nullsummenspiel verkommen. Wenn ein Projekt in einer bestimmten Sprache entwickelt wird, reicht schon der Umstand, dass eine andere Sprache nicht genutzt wird, um deren Notwendigkeit infrage zu stellen. Tatsächlich sind die Optionen viel vielfältiger, und selbst bei einer Neuimplementierung stünden nicht nur Rust, sondern auch Go, D, Lisp, Julia und andere Sprachen zur Auswahl
    • Tatsächlich sind die SQLite-Entwickler offen für ein Rewrite in Rust. Wenn Rust die nötigen Voraussetzungen erfüllt, könnte eine Neuimplementierung in Betracht kommen. Rust-Fans wird sogar empfohlen, direkt mit den SQLite-Entwicklern Kontakt aufzunehmen
    • Es gibt bereits in Rust umgesetzte Projekte wie rqlite und turso
    • Es gibt einen in Go geschriebenen Adapter, mit dem sich SQLite in Golang ohne cgo verwenden lässt. SQLite ist inzwischen nicht nur eine C-Bibliothek, sondern auch ein Datenbank-Dateiformat. Vielleicht erscheint künftig eine Pure-Rust-Implementierung und wird irgendwann zur Hauptimplementierung
    • Mich frustriert die heutige Tendenz, Technologien, die älter als fünf Jahre sind, sofort als veraltet abzutun. Es braucht mehr Respekt für Technologien, die über lange Zeit ausgereift wurden
  • Sichere Sprachen erzeugen bei Array-Zugriffen zusätzliche Branches für Bounds Checks, aber in korrekt funktionierendem Code werden diese Branches in der Praxis nicht ausgeführt. Deshalb ist 100% Branch-Testing schwierig, und genau das hängt mit der Qualitätsstrategie von SQLite zusammen. Diesen neuen Gedankengang fand ich interessant
    • Wenn man sicher weiß, dass dieser Code-Branch niemals ausgeführt wird, muss man ihn dann überhaupt testen? Es wirkt so, als würde man für 100% Testabdeckung Sicherheit opfern
    • In sicheren Sprachen fügt der Compiler automatisch Schutzcode wie if (i >= array_length) panic("index out of bounds") hinzu. Dieser Code selbst ist aber durch den Rust-Compiler gut getestet, daher sollte er kein Problem darstellen. Ich bin mir nicht sicher, ob ich diese Logik richtig verstehe
    • Bei Experten wie Dr. Hipp und bei Projekten wie SQLite scheint dieses Argument durchaus Gewicht zu haben
    • Mit Methoden wie Rusts get_unchecked() sind auch Zugriffe ohne Bounds Check möglich, was Sicherheit und Performance kombinieren kann get_unchecked-Dokumentation
    • Vielleicht ließe sich das Problem entschärfen, indem Branches, die nur bedingt in einen Panic laufen, von der Coverage-Pflicht ausgenommen werden
  • SQLite hält sich die Möglichkeit offen, eines Tages in Rust neu geschrieben zu werden, während Go wegen Einschränkungen rund um assert() eher unwahrscheinlich ist. Für eine Migration nach Rust wären aus dieser Sicht Bedingungen nötig wie: Rust müsste über längere Zeit stabiler und weniger veränderlich werden, für allgemeine Bibliotheksentwicklung geeignet sein, auch auf Embedded-Systemen ohne OS laufen, Tooling für 100% Branch Coverage bieten, über Mechanismen für OOM-Fehlerbehandlung verfügen und die Rolle von C ohne Performanceverlust ersetzen können
    • Rust hat sich seit Rust 1.0 über mehr als zehn Jahre kompatibel weiterentwickelt. Der Unterschied liegt darin, ob man vollständigen Stillstand bei Änderungen erwartet oder mit Änderungen leben kann. Allgemeine Bibliotheksentwicklung ist bereits bewiesen, und Embedded-Unterstützung ohne OS ist klar möglich. Bei Branch Coverage kenne ich mich als Nichtfachmann nicht gut genug aus, aber etwa bei Ferrocene wird daran gearbeitet. Die Rust-Sprache selbst allokiert keinen Speicher, daher kann OOM-Behandlung auf Ebene der Standardbibliothek entschieden werden. Performancefragen können je nach Definition unterschiedlich bewertet werden
    • Wäre es in Go nicht auch möglich, if condition { panic(err) } wie eine Art Assert-Funktion zu verwenden?
  • Die meisten Argumente wirken anfangs plausibel, sind bei genauerem Hinsehen aber nicht vollständig überzeugend. Wenn die Wahl von C um das Jahr 2000 herum sauber begründet ist, sollte es heute nicht ausreichen, einfach das gut gepflegte bestehende Codebase zu akzeptieren? Die zusätzlichen Argumente erscheinen in Teilen widerlegbar
    • Mich würde konkret interessieren, welche Argumente widerlegbar sein sollen
    • Die vorgebrachten Argumente eignen sich zur Verteidigung eines bestehenden Codebases, aber um neue Entwickler dazu zu bringen, statt komplexerer Sprachen C zu wählen, bräuchte es mehr Begründungen
    • (Das betreffende Dokument wurde 2017 verfasst)
    • Vermutlich wurde dieses lange und detaillierte Dokument geschrieben, weil immer wieder viele Fragen der Art „Warum nicht in X neu schreiben?“ auftauchten
  • Projekte, die SQLite automatisiert nach Go übertragen, gibt es schon seit Jahren und sie werden aktiv veröffentlicht modernc.org/sqlite. Auch dieselbe Testsuite wird erfolgreich bestanden. Allerdings ist die Go-Version deutlich langsamer, und oft ist nicht die Geschwindigkeit selbst, sondern der Komfort eines nativen Go-Portings wichtiger. Insgesamt scheint es realistischer, SQLite automatisch aus C zu übersetzen, als es komplett in Go, Rust, Zig, Nim, Swift oder einer anderen Sprache neu zu schreiben
    • Die öffentliche Testsuite wird zwar bestanden, aber SQLite soll zusätzlich noch eine wesentlich intensivere interne Testsuite haben
    • Das Bestehen einer Testsuite bedeutet nicht automatisch, dass keine Bugs existieren; neue Edge Cases oder Performanceprobleme können weiterhin auftreten
  • „Warum wurde SQLite in C entwickelt?“ wird in der offiziellen Dokumentation gut erklärt. Bei der Frage „Warum nicht Rust?“ denke ich eher zuerst: „Warum sollte es unbedingt Rust sein?“
    • Es wird angemerkt, dass diese Fragestellung wohl durch den aufgesetzten Titel entstanden ist
    • Solche Rust-Rewrite-Projekte gibt es bereits: tursodatabase/turso und auch im Blogpost wurde das Warum diskutiert
    • Der Tonfall ist ähnlich wie bei der Frage, warum SQLite nicht in BASIC statt in C geschrieben wurde
  • Je mehr ich über Programmierung, Softwarenutzung und Rewrites lese, desto stärker sehe ich ein Kernproblem: Wenn ein Rewrite nur auf „funktionale Gleichwertigkeit“ abzielt, fehlen leicht die vielen über Jahre angesammelten Sonderbehandlungen und Patches. Am Ende geht die Software dann wieder kaputt oder Dinge, die früher zuverlässig funktionierten, funktionieren nicht mehr. Solche Rewrites brauchen viel mehr Nachdruck und Vorsicht, und ich halte eine 100%ige Wiederherstellung für schwierig. Das gilt auch für wichtige Bibliotheken wie SDL. Mit wiederholt fehlerhaften Releases und frustrierten Nutzern ist zu rechnen. C wird wohl noch lange überleben, selbst wenn Rust zum Mainstream wird. Ein Rewrite sollte keine Standardentscheidung sein
  • Ich finde es noch interessanter, dass DuckDB nicht in Rust, sondern in C++ geschrieben wurde. DuckDB ist ein junges Projekt von 2019, bei dem man eher erwarten könnte, dass Rust gewählt worden wäre, trotzdem fiel die Wahl auf C++. DuckDB ist neu und das Codebase auch deutlich kleiner als das von SQLite
    • Soweit ich gehört habe, hatte das DuckDB-Team großes Vertrauen in C++ und in die Autovektorisierung der Compiler. Damals, also 2019, gab es in Rust noch keine klare High-Level-SIMD-Unterstützung. Von Hand geschriebene SIMD-Codes wollte man nicht pflegen
    • Wenn maximale Performance das Ziel ist, kann C++ meiner Ansicht nach schnellere Binärdateien mit weniger Code erzeugen. Modernes C++ bietet zudem viel Compile-Time-Sicherheit und passt daher gut zu Code wie einer Datenbank
    • Ich denke, mit modernem C++ ist das völlig in Ordnung
  • Auch früher gab es schon viele Debatten über ein SQLite-Rewrite 2021, 2018
    • Der Kommentar von tptacek ist interessant: In früheren Dokumenten gab es einen Abschnitt zu Sicherheit, der in der neuesten Version verschwunden ist. C ist auch für SQLite ein klarer Sicherheitsnachteil. In einer älteren Version hieß es, SQLite sei keine besonders sicherheitskritische Bibliothek. Die Ausführung nicht vertrauenswürdiger SQL-Anweisungen sei ohnehin schon das größere Problem; beim Import externer Dateien verhinderten Abwehrcode und starke Tests Probleme, außerdem gebe es Vorvalidierungsroutinen Web-Archiv von 2021
 
aer0700 2025-10-16

Die Formulierung, dass C auch für SQLite ein Sicherheitsrisiko sei – gilt das selbst dann, wenn man ausreichend gute Tests schreibt und selbst als Entwickler ausreichend erfahren ist? Dass Logik und Entwicklungsprozess das Problem sein können, ist nachvollziehbar, aber dass die Sprache selbst eine Sicherheitslücke sein soll, ist für mich schwer zu verstehen. Tatsächlich gibt es doch kaum Programme, die nicht auf in C geschriebene Infrastruktur angewiesen sind.