10 ポイント 投稿者 GN⁺ 2025-10-16 | 2件のコメント | WhatsAppで共有
  • SQLite は、性能、互換性、少ない依存関係、安定性を理由に、初期(2000年)から C 言語で開発されている
  • C はほぼすべての OS と言語から利用でき、特に 低レベルライブラリ として高速な動作を支える
  • オブジェクト指向言語ではなく C を選んだ理由は、拡張性、さまざまな言語からの呼び出しやすさ、そして開発当時の C++ と Java の未成熟さ にある
  • SQLite は 依存関係がほとんどない単一ファイル構造 を持ち、C 標準ライブラリの最小限の関数だけを使用する
  • Rust や Go のような "安全な言語" での再実装も議論されているが、品質管理、性能、ライブラリとしての呼び出しやすさなどの面で 依然として C が優位 である

1. C が最適な選択である理由

  • SQLite は 2000年5月29日の初回開発以来、現在まで C 言語で維持 されている
    • 現時点では他の言語に書き換える計画はない
  • C は ハードウェアに近い制御力 を持ちながら移植性にも優れ、「ポータブルなアセンブリ言語」と呼ばれる
  • 他の言語が「C と同じくらい速い」と主張することはあっても、C より速いと主張する言語はない

1.1. 性能

  • SQLite のような 低レベルライブラリ は頻繁に呼び出されるため、非常に高速に動作することが求められる
  • C 言語は高速なコードを書くのに適しており、移植性が高いうえにハードウェアへ密接にアクセスできる
  • 他の現代的な言語も「C と同じくらい速い」と主張するが、汎用プログラミングにおいて C より速いと断言できる言語はない
  • C はメモリと CPU 資源を細かく制御できるため、ファイルシステムより 35% 高速な性能 を示すこともある

1.2. 互換性

  • ほぼすべてのシステムが C で書かれたライブラリ を呼び出せる
  • たとえば Android(Java ベース)でもアダプタを通じて SQLite を利用できる
  • もし SQLite が Java で書かれていたなら、iPhone(Objective-C, Swift)では使えず、汎用性が大きく低下していただろう

1.3. 低い依存関係

  • C ライブラリ として開発されているため、ランタイム依存関係が非常に少ない
  • 最小構成では標準 C ライブラリのごく基本的な関数(memcmp(), memcpy(), memmove(), memset(), strcmp(), strlen(), strncmp())だけを使う
  • より完全なビルドでも malloc(), free(), ファイル入出力など、少数の依存関係しか持たない
  • 現代的な言語では、多数の巨大なランタイム何千ものインターフェース が必要になることが多い

1.4. 安定性

  • C は 古く、変化の少ない退屈な言語 だが、それはそのまま 予測可能性と安定性 を意味する
  • SQLite のような 小さく、高速で、信頼できるデータベースエンジン を作るには、仕様が頻繁に変わらない言語が適している
  • 言語仕様や実装がたびたび変わると、SQLite の安定性には不利になる

2. なぜオブジェクト指向言語で書かれなかったのか

  • 一部の開発者は、オブジェクト指向でなければ SQLite のような複雑なシステムを実装しにくいと考えるが、C と比べて C++ や Java でライブラリを作ると、他の言語から呼び出しにくくなる
  • Haskell、Java など多様な言語を支援するために、C ライブラリを選ぶ ことは妥当だった
  • オブジェクト指向は言語ではなく設計パターン であり、特定の言語に限定されるものではない
    • C でも構造体と関数ポインタでオブジェクト指向パターンを実装できる
  • オブジェクト指向が常に最適な構造とは限らず、手続き型コードのほうが明快で管理しやすく、より速い結果を得られることもある
  • SQLite 開発初期(2000年ごろ)には
    • Java は未成熟で
    • C++ は コンパイラ間の互換性問題が深刻 だった
      → 当時は C が 最も実用的で安全な選択 だった
  • 現在でも SQLite を書き換えるだけの利点は乏しい

3. なぜ "安全な言語" で書かれなかったのか

  • 近年は Rust や Go のような 安全なプログラミング言語 への関心が高いが、SQLite が最初に開発された当時(最初の10年間)は存在していなかった
  • Go や Rust で書き直した場合、バグが増えたり、性能が低下したりする可能性がある
  • これらの言語はメモリチェックなどの追加分岐(branch)コードを挿入するが、SQLite の品質戦略では 100% ブランチカバレッジ が重要であり、この点を満たせていない
  • 安全な言語は out-of-memory 状況で主にプログラムを停止させるが、SQLite は メモリ不足の状況からでも復旧できるよう設計 されている
  • Rust、Go などは依然として新しい言語であり、継続的な発展が必要である
  • そのため SQLite 開発チームは安全な言語の発展を歓迎しつつも、SQLite の実装では依然として 実証済みの C の安定性 を重視している

それでも、いつか Rust で再実装される可能性 はある。Go は assert() を好まないため、Go で書かれる可能性は低い

  • ただし Rust で書かれるには前提条件がある:
    • Rust がより成熟し、変化の周期が遅くなって「古くて退屈な言語」になること
    • 複数の言語から呼び出せる汎用ライブラリ を作れることが証明されること
    • 組み込みなど OS のないデバイスでも動作するオブジェクトコード を生成できること
    • コンパイル済みバイナリに対する 100% ブランチカバレッジのテストツール が整備されること
    • OOM(メモリ不足)エラーから復旧可能 であること
    • SQLite で C が処理しているすべての作業を 性能低下なしに Rust が実行できること
  • もし Rust 愛好家(rustacean)が、上記の条件はすでに満たされており、SQLite を Rust で再コーディングすべきだと考えるなら、SQLite 開発者に直接連絡して意見を伝えてみるとよい

2件のコメント

 
GN⁺ 2025-10-16
Hacker Newsの意見
  • 安全なプログラミング言語は最初の10年間には存在していなかったとしても、SQLiteをGoやRustで再実装すれば、修正できるバグよりも多くのバグを新たに生み、速度も遅くなる可能性が高いと思う。すでに膨大な時間とテストを経てバグのないコードが完成しているなら、変更率が低い状況では、どの言語で書かれていても関係ない。極端に言えばアセンブリ言語でも構わないレベルだ
    • 「変更率が低いと問題が少ない」という点はGoogle Security Blogでも説明されている。ここではメモリ安全性の問題の大半は新しいコードで発生し、コードは時間が経つほど安全になると主張している 関連リンク
    • Rust側ではTursoのようなプロジェクトがかなり活発に動いている Turso
    • Linuxの基本ユーティリティをRustで書き直すべきではないと主張する人たちもいる。何十年も使われ、ほとんどのバグがすでに取り除かれたソフトウェアを、あえて書き直す必要はないと考えている
    • Zigは一部のCコードの置き換えに向いていると思う。Pythonや既存のCバイナリとも相性が良い。Goの哲学は良いが、最適化が難しく、実力のある開発者が必要だという限界があった。Rustも使えるだろうが、既存のCを使い続けながらZigを段階的に導入するほうがはるかに容易だった。Cコードからバグを完全に除去することはできないが、Rustへの移行は現実的に難しいと感じる
    • すでにGoへポーティングされたsqlite実装も存在する cznic/sqlite
  • SQLiteが最初に開発された当時、Cが最適な選択だった理由や現在の利点に加えて、あえて別の言語でSQLiteを書き直す特別な理由はないと思う。軽量SQLデータベースを実装すること自体は誰にでも可能で、Rust、C++、Go、Lispなど好きな言語で新しい実装を作ればよい。既存のCでうまく動いている実装をむやみに捨て、25年以上CでSQLiteを維持してきた開発者に、新しい言語を無理に学ばせて最初から作り直させる必要はない
    • 多くの言語ファンダムには、自分の望むものを他人に押し付ける傾向があり、言語採用が一種のゼロサム争いのように歪んでいる部分があると感じる。あるプロジェクトが特定の言語で開発されていると、その言語を使っていないだけで他の言語の必要性を問うようになる。実際には選択肢はもっと多様で、別の言語で書き直すとしても候補にはRust、Go、D、Lisp、Juliaなどさまざまな言語が並ぶ
    • 実際、SQLite開発者はRustへのリライトに前向きであり、Rustが必要な前提条件を満たせば作り直す可能性もある。Rust支持者ならSQLite開発者に直接連絡してみるとよいという案内もある
    • すでにRustで実装されたrqliteやTursoのようなプロジェクトがある
    • Goで書かれたアダプタがあるため、golangではcgoなしでsqliteを使える。いまやsqliteは単なるCライブラリであるだけでなく、データベースファイル形式でもある。今後pure Rust実装が現れ、いつかそれがメイン実装になる可能性もあると思う
    • 最近は5年以上前の技術を古いと言って切り捨てる風潮があり、もどかしさを感じる。長年磨かれてきた技術への敬意がもっと必要だと思う
  • 安全な言語は配列アクセス時に境界チェック用の追加分岐を生成するが、正しいコードではそうした分岐は実際には実行されない。つまり100%ブランチテストが難しく、この点がSQLiteの品質戦略と関係している。こうした新しい理屈を聞けて興味深い
    • もしそのコード分岐が絶対に実行されないと確信できるなら、そもそもその部分をテストする必要はないのではないか。テストカバレッジ100%のために安全性を犠牲にしているようにも感じる
    • 安全な言語ではコンパイラが自動で if (i >= array_length) panic("index out of bounds") のような防御コードを追加するが、そのコード自体はRustコンパイラが十分にテストしているのだから心配はいらないのではないか。この理屈を自分が正しく理解しているのか気になる
    • Dr. Hippのような専門家、そしてsqliteのようなプロジェクトであれば、こうした論拠にも一理あると思う
    • Rustの get_unchecked() のような方法を使えば、bounds checkなしのアクセスも可能で、これによって安全性を保ちつつ性能を高められる get_uncheckedドキュメント
    • 条件付きでpanicに落ちる分岐については、カバレッジ必須の対象から外すことでこの問題を軽減できないかと気になる
  • SQLiteは将来Rustで書き直される可能性を残しており、Goはassert()まわりの制約のため可能性が低い。Rustへ移行するには、Rustがより長い期間安定して変化が少なくなること、汎用ライブラリ作成に適していること、OSなしの組み込み環境でも動くこと、100%ブランチカバレッジ用のツーリングがあること、OOMエラー処理機構が備わっていること、性能低下なしにCの役割を代替できること、といった前提条件が必要だと思う
    • Rustは1.0以降10年以上にわたって互換性を保ちながら進化してきた。完全に変化が止まることを望む人と、変化してもよいと考える人との違いがある。汎用ライブラリ開発への適性はすでに実証されており、OSなしの組み込み対応も明確に可能だ。ブランチカバレッジについては自分は非専門家なので詳しくないが、Ferroceneなどで関連作業が進んでいる。Rust言語自体はメモリ割り当てを行わないので、OOM処理は標準ライブラリのレベルで決められる。性能問題は定義の仕方次第で解釈が変わりうる
    • Goでも if condition { panic(err) } 構文をassert関数のように使えるのではないかと気になる
  • 大半の主張は最初はもっともらしく見えるが、細かく検討すると完全ではないと感じる。Cを2000年ごろに選んだ理由だけを明確に説明できれば、現在はよく磨かれたコードベースを受け入れればよいのではないかと思う。補助的な論拠には反論できる部分がある
    • 具体的にどの主張が反論可能なのか聞きたい
    • 示された論拠は過去のコードベース維持には使えるが、新しい開発者に複雑な言語ではなくCを採用させるには、さらに多くの根拠が必要だ
    • (この文書は2017年に書かれたもの)
    • 長年にわたり繰り返し寄せられる「なぜXで書き直さないのか」という質問に対応するため、詳細で長い文書を書いたのだろうと推測する
  • SQLiteを自動化でGoへ移したプロジェクトは、すでに何年も前から存在し活発に配布されている modernc.org/sqlite。同じテストスイートにもよく合格している。ただしGo版はかなり遅く、速度そのものよりもGoネイティブ移植の利便性のほうが重要な場面が多い。結論として、SQLiteをGoやRust、Zig、Nim、Swiftなどで一から書き直すより、Cから自動翻訳する方式のほうが現実的だと思う
    • 公開テストスイートには合格しても、SQLiteにははるかに厳しい社内テストスイートもあると聞く
    • テストスイートに合格したことはバグがないことを意味せず、新しいエッジケースや性能問題は残りうると思う
  • 「なぜSQLiteはCで開発されたのか」は公式文書でよく説明されているが、「なぜRustではないのか」と聞かれると、むしろ「なぜRustでなければならないのか?」が先に思い浮かぶ
    • これは上に被せられたタイトルのせいだという意見
    • すでにこの種のRustリライトプロジェクトがある: tursodatabase/turso と、Whyについても論じたブログ記事
    • SQLiteをなぜBASICではなくCで書いたのかと問うこともできる、という論調だ
  • コーディングやソフトウェア利用、そしてリライトに関する記事を多く見るほど、「機能同等性」だけを目標にリライトすると、それまで蓄積された膨大な例外処理やパッチが抜け落ちやすいという問題がある。結局またソフトウェアが壊れたり、以前はうまく動いていたものが壊れることになりがちだ。こうしたリライトには十分な強調と慎重さが必要で、100%の再現は難しいと思う。SDLのような重要ライブラリも同様だ。繰り返し壊れるリリースやユーザーの不満が予想される。CはRustが主流になった後も長く生き残ると思う。リライトはデフォルトの選択肢であるべきではない
  • DuckDBがRustではなくC++で書かれている点のほうが興味深い。DuckDBは2019年に登場した新しいプロジェクトで、Rustを採用していてもおかしくなかったが、最終的にはC++を選んだ。DuckDBは新しく、コードベースもSQLiteよりはるかに小さい
    • DuckDB開発陣はC++に自信があり、コンパイラの自動ベクトル化機能を信頼して選んだと聞く。当時(2019年)のRustには明確な高水準SIMDサポートがなかった。手書きのSIMDコードを保守したくなかったのだろう
    • 最大性能が目標なら、C++のほうがより少ないコードでより高速なバイナリを作れると思う。最新のC++はコンパイル時安全性も高く、DBのようなコードに向いている
    • モダンC++で書くなら問題ないと思う
  • 以前からSQLiteリライト論争は何度もあった 2021年2018年
    • tptacekのコメントが興味深い。以前の文書にはセキュリティに関する段落があったが、最新版では消えている。CはSQLiteにとっても明確なセキュリティ脆弱性だ。以前の版には「SQLiteはそれほどセキュリティ敏感なライブラリではない」という説明があった。信頼できないSQLを実行すること自体がすでにもっと大きな問題であり、外部ファイルのインポートについては防御コードと強力なテストで問題を防ぎ、事前検証ルーチンも存在するとされていた 2021年のWebアーカイブ文書
 
aer0700 2025-10-16

CはSQLiteにとってもセキュリティリスクだ、という一節がありますが、十分にテストを書き、十分に熟練した開発者であっても同じなのでしょうか。ロジックや開発プロセスに問題がある可能性はあると思いますが、言語そのものがセキュリティ脆弱性だというのは理解しにくいです。実際、Cで書かれたインフラに依存しないプログラムはほとんどないのではないでしょうか。