世界を動かす技術を、日本語で。

SQLiteはなぜC言語でコーディングされているのか

2025年10月15日原文(sqlite.org)

概要

  • SQLite は2000年から C言語 で実装されているデータベースライブラリ
  • C言語 が選ばれている理由は 性能、互換性、依存性の低さ、安定性
  • オブジェクト指向言語や「安全な」言語への移行は現状メリットが少ない
  • 「安全な」言語(例:Rust)への移行には 多くの課題 が残る
  • 今後も C言語 で開発が継続される予定

SQLiteがCで実装されている理由

  • C言語高速なコード が書ける「ポータブルなアセンブリ言語」的存在
  • ほぼ全てのシステムで C言語ライブラリ を呼び出せる互換性
  • 依存性が極めて低い ため、最小構成なら標準Cライブラリの数関数のみで動作
  • C言語 は古くて安定しており、仕様変更による影響が小さい
  • SQLite のような低レイヤーで広く使われるライブラリに最適な選択

オブジェクト指向言語で実装されていない理由

  • C++やJava で書かれたライブラリは、同じ言語で書かれたアプリからしか使いにくい
  • C言語 なら、ほぼ全ての言語から呼び出し可能なライブラリ構築が可能
  • オブジェクト指向は設計手法であり、 C言語でも実現可能
  • 問題の分解方法として「手続き型」が有利な場合も多い
  • 初期開発時(2000年頃) はC++やJavaの成熟度・互換性に課題あり

「安全な」言語で実装されていない理由

  • RustやGo などの「安全な」言語はSQLite開発開始時には存在しなかった

  • 移植すると 新たなバグや性能低下 のリスク

  • 「安全な」言語は配列範囲チェック等のため 分岐が増え、完全なブランチテストが難しい

  • OOM(メモリ不足) 時の挙動がSQLite設計と異なることが多い

  • Rust は今後の候補になる可能性もあるが、下記課題の解決が必要

    • 言語仕様の安定化
    • 他言語からの呼び出し互換性
    • 非OS環境・組み込み機器での動作
    • 100%ブランチカバレッジの実現
    • OOM時の優雅なリカバリー
    • C並みの性能
  • Rustacean (Rust愛好者)は、SQLite開発者に直接提案可能

今後の方針

  • C言語 による開発継続方針
  • Rust への移行は慎重に検討されるが、現時点で計画なし
  • 「安全な」言語の進化には期待しつつも、 信頼性と実績重視 の姿勢

Hackerたちの意見

以前の重要な議論が2つ、このページにありますね。 https://news.ycombinator.com/item?id=28278859 - 2021年8月 https://news.ycombinator.com/item?id=16585120 - 2018年3月

tptacekのコメントが気になるな(https://news.ycombinator.com/item?id=28279426)。「このページの「セキュリティ」に関する段落は、他の議論を損なっている。実際、CはSQLiteにとって明らかなセキュリティリスクだ。」現在のドキュメントにはセキュリティに関する段落も「セキュリティ」という言葉すら一度もない。2021年版のドキュメントには、今はもう見当たらないこの文が含まれていた:「安全な言語は、セキュリティの脆弱性を防ぐのに役立つとしばしば謳われる。確かにそうだけど、SQLiteは特にセキュリティに敏感なライブラリではない。アプリケーションが信頼できないSQLを実行しているなら、すでにもっと大きなセキュリティ問題(SQLインジェクション)があるし、どんな「安全な」言語でもそれを解決することはできない。アプリケーションが信頼できないソースから完全なバイナリSQLiteデータベースファイルをインポートすることがあるのは事実だけど、そのようなインポートは攻撃ベクトルになる可能性がある。しかし、SQLiteのそのコードパスは限られていて、非常によくテストされている。そして、信頼できないデータベースを読み取ることを望むアプリケーションには、使用前に可能な攻撃を検出するのに役立つ事前検証ルーチンが用意されている。」 https://web.archive.org/web/20210825025834/https%3A//www.sql...

安全なプログラミング言語は、配列のアクセスが範囲内であることを確認するために、追加のマシン分岐を挿入します。正しいコードでは、これらの分岐は決して実行されません。つまり、マシンコードは100%分岐テストされることはなく、これはSQLiteの品質戦略の重要な要素です。ふむ、こんな新しい議論を聞くのは珍しいね。シェアしてくれてありがとう。

その分岐は、範囲外に出ることが証明されていないから、そもそも存在しないことが多いと思います。コンパイラが範囲を破ることができないことを知っていることを確実にする方法があります。

この問題は、無条件でパニックに至る分岐のカバレッジを要求しないことで軽減できるのかな?それとも、正しいコードでは決して発生しないことを示すマークをその分岐に付けることができるのかな?

つまり、安全な言語が生成するのは、例えばこういうこと? // 疑似コード if (i >= array_length) panic("インデックスが範囲外") 実際にコードが正しければ、これは決して実行されない?でも(私が正しく理解していれば)これはコンパイラによって暗黙的に追加されたチェックだよね。だから、異議はこの自動生成されたコードの正しさを疑うことに帰着するし、コンパイラの正しさを信頼していないことが前提になってる?でも、Rustコンパイラ自体はこれらのチェックが機能することを徹底的にテストしているはずだよね?もし私が議論を誤解していたら、誰か訂正してほしいな。

これは、ほとんどの人やプロジェクトからは受け入れられないような議論だけど、ヒップ博士からのは別だし、SQLiteも普通のプロジェクトじゃないからね。

安全だとわかっているなら、get_unchecked()みたいなメソッドを使って境界チェックを避けられない?

その議論にはあまり説得力を感じないな。もしコードのブランチが絶対に取られないと確信しているなら、それをテストする必要もないと自信を持つべきだと思う。これは安全性を犠牲にして無理に100%のテストカバレッジを追い求めているように感じる。チェックを省いたからといってコードの質が向上するわけじゃないし、テストカバレッジが上がるだけなんだよね。

考えるのは面白いけど(ページ全体がよく考えられてるね)、その主張は検証に耐えないと思う。自動的な境界チェックが失敗したら、プログラムはその分岐なしで未定義の動作を示してしまうし、未定義の動作(UB)は、何かしらの明確な処理をする到達不能な分岐よりも厄介だよね。Cでのシンプルな配列アクセス:arr[i] = 123; は、こう考えられるかも:if (i >= array_length) UB(); else arr[i] = 123; ここで「UB」関数は何でもできるからね。ソフトウェアの徹底的なテストと形式的な検証の観点からは、安全な言語の同等のものが欲しいな:if (i >= array_length) panic(); else arr[i] = 123; ...だって、少なくとも到達不能な条件が発生した場合に何が起こるか考えられるから。ヒップ博士は「SQLiteをGoで再コーディングするのは難しい、なぜならGoはassert()が嫌いだから」と言ってるけど、これはSQLiteが到達不能な条件を守るためにassert文を使っていることを示唆してるよね。彼のテストインフラは、到達不能なassert分岐を免除する方法があるはずなのに、なぜ境界チェック(未定義の動作が発生しないことを確認するだけのもの)が同じように扱えないんだろう?

「間違ったコードでは分岐が発生するけど、分岐のないコードは予測できない動作をするだけなんだ。シートベルトみたいなもんだよ。例えば、4ブロック運転してからシートベルトが必要になるケースがあったらどうする?まあ、そのための明確なテストはあるけど、全てをテストすることはできない。4ブロック運転して右折して、半ブロック後に何かにぶつかる場合をテストしてないし。もういいや、シートベルトを外して、シートベルトがちゃんと機能するかどうか不確実なこの狂った未テストの領域をなくそう!」

新しい理由は、全く意味がないからだよ。Cでは、配列アクセスごとに暗黙の「ブランチ」があるんだ。それはアクセス違反と呼ばれている。コードベースのすべての配列アクセスでセグフォルトをテストしてるの?してない?じゃあ、実際には100%のブランチカバレッジはないってことだね。

Hacker Newsで議論の続きを見る