Visitar URL original
SQLCipherStatement.getColumnType returns Cursor.FIELD_TYPE_NULL (0) instead of SQLITE_DATA_NULL (5), breaking room3-sqlite-wrapper on NULL columns · Issue #93 · sqlcipher/sqlcipher-android · GitHub
Skip to content

SQLCipherStatement.getColumnType returns Cursor.FIELD_TYPE_NULL (0) instead of SQLITE_DATA_NULL (5), breaking room3-sqlite-wrapper on NULL columns #93

Closed

Description

@ZoeyJones

Summary

net.zetetic.database.sqlcipher.driver.SQLCipherStatement implements androidx.sqlite.SQLiteStatement, and its getColumnType(int) should return androidx.sqlite's SQLITE_DATA_* constants. It currently returns android.database.Cursor.getType(index) unchanged. The two agree for INTEGER, FLOAT, TEXT and BLOB (1–4), but NULL is Cursor.FIELD_TYPE_NULL = 0 versus SQLITE_DATA_NULL = 5. So every NULL column reports 0.

Affected: 4.18.0 (where the Room 3 driver was added), 4.19.0, and master @ bc71616.

Impact

Code that switches on the contract constants fails on the first NULL value. With Room 3's room3-sqlite-wrapper, roomDatabase.getSupportWrapper().query(...) throws while building the Cursor:

java.lang.IllegalStateException: Unknown column type: 0
  at androidx.room3.support.RoomSupportSQLite.toCursor(RoomSupportSQLite.kt:126)
  at androidx.room3.support.RoomSupportSQLiteDatabase$query$1$1.invokeSuspend(RoomSupportSQLiteDatabase.kt:154)
  at androidx.room3.coroutines.PassthroughConnection.usePrepared(PassthroughConnectionPool.kt:165)
  ...

We hit this in a production app migrating to Room 3: its ContentProvider queries go through getSupportWrapper(), and they crashed on rows with NULL columns. Room-generated DAO code uses isNull(), so plain DAO usage isn't affected.

Minimal reproduction

System.loadLibrary("sqlcipher")
val connection = SQLCipherDriver("pw".toByteArray(), null, null).open(dbFile.absolutePath)
connection.prepare("SELECT NULL").use { statement ->
    statement.step()
    statement.getColumnType(0) // expected SQLITE_DATA_NULL (5), actual 0
}

Reference fix

A fix with two instrumented tests is in a personal fork. It is not submitted as a PR here, pending your contributor-agreement process: ZoeyJones#1

  • It maps each Cursor.FIELD_TYPE_* to the matching SQLite.SQLITE_DATA_* explicitly, and throws on an unknown value.
  • The tests fail on unmodified master (expected:<5> but was:<0>, and the Unknown column type: 0 above).
  • With the fix, the whole net.zetetic.database.sqlcipher.driver package passes: 37/37 on an API 36 arm64 emulator.

Thank you for maintaining SQLCipher for Android.

Activity

  1. developernotes commented on Sep 25, 2026

    @developernotes
    Member

    Hi @ZoeyJones,

    Thanks for submitting this, it's looks good. Would you mind contacting us at support@zetetic.net to sign a contributor agreement so this change can be merged in?

  2. ZoeyJones commented on Sep 27, 2026

    @ZoeyJones
    ContributorAuthor

    Contacted over gmail.

  3. ZoeyJones commented on Sep 28, 2026

    @ZoeyJones
    ContributorAuthor

    Signed the contributor agreement over DocuSign. And here is the pr #94

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions