"Attempt to get length of null array" when calling SQLiteDatabase.delete()

When calling:

delete("foo", "_id in (123, 456)", null)

I noticed (in crashlytics) this error:

java.lang.NullPointerException: Attempt to get length of null array
       at net.zetetic.database.sqlcipher.SQLiteDatabase.delete(SQLiteDatabase.java)

I guess this is because of passing null whereArgs, but this should be fine, right?

This particular code path is rarely encountered in my app, so I don’t know if it’s happening every time, or perhaps depends on the db file.

Any ideas why? Should I just use whereArgs = empyArray() to be safe, or will that make no difference?

Hi @marcardar,

Do you have a scenario where this fails consistently? What version of the library are you using? I just tried out this test within the suite and it succeeds for me:

  @Test
  public void shouldSupportDeleteWithNullWhereArgs(){
    long rowsFound = -1L;
    database.execSQL("create table t1(a,b);");
    database.execSQL("insert into t1(a,b) values(?,?)", new Object[]{1, 2});
    database.execSQL("insert into t1(a,b) values(?,?)", new Object[]{3, 4});
    long rowsDeleted = database.delete("t1", "a in (1, 3)", null);
    assertThat(rowsDeleted, is(2L));
    Cursor cursor = database.rawQuery("select count(*) from t1;", null);
    if(cursor != null && cursor.moveToNext()){
      rowsFound = cursor.getLong(0);
      cursor.close();
    }
    assertThat(rowsFound, is(0L));
  }

I’m using v4.6.1 (android). How about this line:

Hi @marcardar,

Great catch, that requires calling the delete method with a parameter assigned to null, slightly different than the example above. I’ve just pushed up a fix with test that will be included in the next public SQLCipher release.

Thanks Nick. But aren’t both my case and your case passing in null whereArgs? Oh, you mean one is calling the Object[] whereArgs and the other is calling the String[] whereArgs overload?

Maybe I’m getting the NPE because my database instance is typed as SupportSQLiteDatabase? Perhaps in your test it’s net.zetetic.database.sqlcipher.SQLiteDatabase?

Hi @marcardar,

Correct, when passing an explicit null parameter this [1] delete method would be invoked, whereas passing Object[] whereArgs = null would dispatch to this [2] version of delete.


  1. sqlcipher-android/sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SQLiteDatabase.java at master · sqlcipher/sqlcipher-android · GitHub ↩︎

  2. sqlcipher-android/sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SQLiteDatabase.java at master · sqlcipher/sqlcipher-android · GitHub ↩︎

Hi Nick, but it looks like both your and my code are passing in null (neither are specifying the type of null). The distinction is whether the overridden method (line 1182) is called or whether the other one is called. That is determined by the declared type of the database instance. In my case, the declared type is SupportSQLiteDatabase and so the overridden method is called, which is why the NPE occurred. You’ve since fixed that. So I’m pretty sure your test case database declared type is net.zetetic.database.sqlcipher.SQLiteDatabase, not SupportSQLiteDatabase.

Hi @marcardar,

I made my comments based on your original reference to net.zetetic.database.sqlcipher.SQLiteDatabase:

I was only noting the distinction of the method dispatch varying based on how the arguments were provided at the call site. Please let me know if this doesn’t address your concerns.

The arguments were provided exactly the same way.

My code:

database.delete("foo", "_id in (123, 456)", null)

Your code:

database.delete("t1", "a in (1, 3)", null)

My point is that the reason why one results in an NPE and the other doesn’t, is the static typing of the database instance.

Hi @marcardar,

Yes, I agree the test example follows your supplied argument structure. I suspect while you may be operating with a SupportSQLiteDatabase, it is delegating the call to an internal net.zetetic.database.sqlcipher.SQLiteDatabase which is why that type showed up in your Crashlytics report in association with the NPE.

It’s to do with static binding. From ChatGPT:

Method Resolution:
Compile-Time Method Selection (Static Binding):

Since instance is declared as a Superclass, at compile time, Java will look for the foo method in Superclass.
The Superclass has a foo(Object[] args) method, so this method will be selected at compile time.
Method Overloading is resolved at compile time based on the declared type of the reference (Superclass in this case), not the runtime type (Subclass).
Runtime Behavior:

Even though instance is actually a Subclass object, method overloading depends on the compile-time type (Superclass), so the foo(Object[] args) method from the Superclass will be called.