DB

【SQLite】ハマリやすい例外と対策方法まとめ

SQLiteに関連するエラー対処法まとめです。

Row too big to fit into CursorWindow

SQLiteを使用したAndroidアプリで、上記の例外が発生した。
logcatコマンドでAndroidのログを出力すると下記のようなスタックトレースを発見。

08-21 17:03:44.351 18980 19077 W CursorWindow: Window is full: requested allocation 2097155 bytes, free space 1949823 bytes, window size 2097152 bytes
:
08-21 17:03:44.369 18980 19077 E SQLiteQuery: exception: Row too big to fit into CursorWindow requiredPos=0, totalRows=1; query: {実際のSQL}
08-21 17:03:44.369 18980 19077 W System.err: java.sql.SQLException: Problems executing Android query: {実際のSQL}
08-21 17:03:44.369 18980 19077 W System.err:    at com.j256.ormlite.misc.SqlExceptionUtil.create(SqlExceptionUtil.java:22)
08-21 17:03:44.369 18980 19077 W System.err:    at com.j256.ormlite.android.AndroidCompiledStatement.getCursor(AndroidCompiledStatement.java:184)
08-21 17:03:44.369 18980 19077 W System.err:    at com.j256.ormlite.android.AndroidCompiledStatement.runQuery(AndroidCompiledStatement.java:65)
08-21 17:03:44.369 18980 19077 W System.err:    at com.j256.ormlite.stmt.SelectIterator.(SelectIterator.java:55)
08-21 17:03:44.369 18980 19077 W System.err:    at com.j256.ormlite.stmt.StatementExecutor.buildIterator(StatementExecutor.java:247)
08-21 17:03:44.369 18980 19077 W System.err:    at com.j256.ormlite.stmt.StatementExecutor.query(StatementExecutor.java:196)
08-21 17:03:44.369 18980 19077 W System.err:    at com.j256.ormlite.dao.BaseDaoImpl.query(BaseDaoImpl.java:265)
:
08-21 17:03:44.369 18980 19077 W System.err:    at roboguice.util.SafeAsyncTask$Task.doCall(SafeAsyncTask.java:204)
08-21 17:03:44.369 18980 19077 W System.err:    at roboguice.util.SafeAsyncTask$Task.call(SafeAsyncTask.java:170)
08-21 17:03:44.369 18980 19077 W System.err:    at roboguice.util.SafeAsyncTask$Task.call(SafeAsyncTask.java:158)
08-21 17:03:44.369 18980 19077 W System.err:    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
08-21 17:03:44.369 18980 19077 W System.err:    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
08-21 17:03:44.369 18980 19077 W System.err:    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
08-21 17:03:44.369 18980 19077 W System.err:    at java.lang.Thread.run(Thread.java:764)
08-21 17:03:44.370 18980 19077 W System.err: Caused by: android.database.sqlite.SQLiteBlobTooBigException: Row too big to fit into CursorWindow requiredPos=0, totalRows=1
08-21 17:03:44.370 18980 19077 W System.err:    at android.database.sqlite.SQLiteConnection.nativeExecuteForCursorWindow(Native Method)
08-21 17:03:44.370 18980 19077 W System.err:    at android.database.sqlite.SQLiteConnection.executeForCursorWindow(SQLiteConnection.java:859)
08-21 17:03:44.370 18980 19077 W System.err:    at android.database.sqlite.SQLiteSession.executeForCursorWindow(SQLiteSession.java:864)
08-21 17:03:44.370 18980 19077 W System.err:    at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:62)
08-21 17:03:44.370 18980 19077 W System.err:    at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:149)
08-21 17:03:44.370 18980 19077 W System.err:    at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:137)
08-21 17:03:44.370 18980 19077 W System.err:    at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:220)
08-21 17:03:44.370 18980 19077 W System.err:    at android.database.AbstractCursor.moveToFirst(AbstractCursor.java:259)
08-21 17:03:44.370 18980 19077 W System.err:    at com.j256.ormlite.android.AndroidCompiledStatement.getCursor(AndroidCompiledStatement.java:181)
08-21 17:03:44.370 18980 19077 W System.err:    ... 17 more

原因

直訳すると「行が大きすぎてCursorWindowに収まりません」と言われている。CursorWindowと言われると馴染みがないが、AndroidでDBから取得したデータを保持するためのバッファーと言うとしっくりくるだろうか。Blobを含むその行データ(レコード)がでかすぎるということ。

スタックトレースの1行目に注目してみると、何やら数値が複数出ている。これは「要求されたバイト数:2097155バイト、空き領域:1949823バイト、ウィンドウサイズ設定値:2097152バイト」という意味で空き領域に対して要求されたサイズがでかすぎるから発生する。

このエラーはDB登録時ではなく、取得時に発生する。

対策

取れる対策は2つ。

  • そんなデカいサイズのデータを登録しない
  • CursorWindowの設定値を増やす

そもそもそんなでかいデータが1行に入るようなデータの持ち方はすべきではなく、仕様を見直すか圧縮して登録する方が安全。やむを得ず登録する場合は「CursorWindowの設定値を増やす」方法がある。AppContext.javaあたりで下記のソースを記述する。
※値はサンプル

Field field = CursorWindow.class.getDeclaredField("sCursorWindowSize");
field.setAccessible(true);
field.set(null, 100 * 1024 * 1024); //100MBに設定

 

 
他にもあれば追記していきます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です