ディスクに空きがない時にSQLiteへINSERTしようとした場合の挙動
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
- サポートされていない操作が行われたときに発生する。普段はまず気にしないで良いと思う。
ぶっちゃけAndroidのSQLiteを使ってみて感じたのは、無視できない頻度でエラーを吐いてくるので、どうなのかなぁという印象。現在開発中アプリがinsert/deleteを頻繁に繰り返すようになっているので、適当な頻度でvacuum・reindexが必要なのかもしれない。