読者です 読者をやめる 読者になる 読者になる

ディスクに空きがない時にSQLiteへINSERTしようとした場合の挙動

Android

Androidでディスクに空きがない時にSQLiteへINSERTしようとした場合の挙動を確認してみた。てか、どんな挙動するのかぐらいドキュメントに書いとけよ>Google

結果として、以下の2つのタイミングでSQLiteDiskIOExceptionが発生することが確認できた。

  • 書き込み可能なDBを開こうとした(SQLiteDatabaseOpenHelper.getWritableDatabase()を呼んだ)タイミング。
    • つまりディスク容量が少ない時は書き込み可能なDBを開くことはできない。
  • データをコミットしようとした(SQLiteDatabase.endTransaction()を呼んだ)タイミング
    • 明示的にトランザクションを開始していない場合は、INSERTしようとした時点で落ちると思う(試してない)。
    • DBを開く時点ではディスクが空いてたんだけど、書き込みをする時点でディスクに空きが無くなったケースが該当する。

おまけ

どのようなケースの場合にどのExceptionがthrowされるのかドキュメントに明記されていないのだが、ある程度ざっくり調べてみた。厳密にはSQLiteネイティブ関数が返すエラーコードとJavaのラッパーを全て把握しないとダメなんだけど、無茶なので諦めた。

SQLiteException
ドキュメントを読む限りではSQL構文エラー。
SQLiteConstraintException
一意制約や外部キーなど制約条件に反したデータ操作を行おうとした場合に発生。
SQLiteDiskIOException
ディスク容量不足などのOSレベルでのファイルI/Oエラーが発生した場合にthrowされる。また原因不明だが、比較的無視できない頻度でCursorがSQLiteDiskIOExceptionをthrowしてくる。その現象が発生すると2度とデータが読めなくなるので、DBファイルが壊れていると思われる。
追記:実験してたアプリが起動中に1秒毎にInsertし続けるもので、終了処理が怪しかったりしたのが原因だったかも。未確認。
SQLiteAbortException
なんらかの原因でSQLiteの処理が中断された場合にthrowされる。Java側のAPIで中断することはできないので、エラーが発生したと考えて良いと思う。
SQLiteDatabaseCorruptException
DBファイルが壊れた場合にthrowされると思われる。SQLiteネイティブエラーコードのSQLITE_CORRUPTがこのExceptionに該当すると思われる。
SQLiteFullException
データ挿入時にDBファイルサイズの上限を超える(2GByteがSQLiteの上限だったっけ?)場合にthrowされると思われる。SQLiteネイティブエラーコードのSQLITE_FULLがこのExceptionに該当すると思われる。
IllegalStateException
beginTransaction()されていないのにendTransaction()が呼ばれた場合など、おかしな状態遷移が発生したときにthrowされる。
UnsupportedOperationException
サポートされていない操作が行われたときに発生する。普段はまず気にしないで良いと思う。

ぶっちゃけAndroidSQLiteを使ってみて感じたのは、無視できない頻度でエラーを吐いてくるので、どうなのかなぁという印象。現在開発中アプリがinsert/deleteを頻繁に繰り返すようになっているので、適当な頻度でvacuum・reindexが必要なのかもしれない。