Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android - When does insertOrThrow() throw an exception?

Before reading the documentation, I would have expected SQLiteDatabase's insertOrThrow() method to have thrown an exception if the insert wasn't successful.

However, the documentation says that the insertOrThrow() method

returns the row ID of the newly inserted row, or -1 if an error occurred

So, if it returns -1 when an error occurs, under what circumstances does it throw an exception?

like image 453
ban-geoengineering Avatar asked Jul 06 '16 13:07

ban-geoengineering


1 Answers

My understanding is that it will return -1 if all the code could be executed properly and a "EXPECTED" error happened. However, not all errors/exceptions are handled and some exceptions may still be triggered:

  • SQLiteDatabaseCorruptException
  • IllegalArgumentException
  • RuntimeException
  • Database is opened for read-only
  • Trying to insert a duplicated key

HERE you can find the implementation of insert() and insertOrThrow().

You can see that both methods are similar. However, insert() handles SQLExceptions for you. insertOrThrow() does not handle any exception and you should do it by yourself.

public long insert(String table, String nullColumnHack, ContentValues values) {
    try {
        return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
    } catch (SQLException e) {
        Log.e(TAG, "Error inserting " + values, e);
        return -1;
    }
}

public long insertOrThrow(String table, String nullColumnHack, ContentValues values)
        throws SQLException {
    return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
}

Both methods calls insertWithOnConflict() which implementation is HERE:

public long insertWithOnConflict(String table, String nullColumnHack,
        ContentValues initialValues, int conflictAlgorithm) {
    acquireReference();
    try {
        // CODE
        try {
            // ONLY HERE I return some value.... Otherwise, I'll raise an exception
            return statement.executeInsert();
        } finally {
            statement.close();
        }
    } finally {
        releaseReference();
    }
}

In the implementation, you will see that it returns "-1" only if statement.executeInsert(); returns something (and it seems that "-1" came from here):

.../frameworks/base/core/jni/android_database_SQLiteConnection.cpp

static jlong nativeExecuteForLastInsertedRowId(JNIEnv* env, jclass clazz,
        jlong connectionPtr, jlong statementPtr) {
    SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
    sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);

    int err = executeNonQuery(env, connection, statement);
    return err == SQLITE_DONE && sqlite3_changes(connection->db) > 0
            ? sqlite3_last_insert_rowid(connection->db) : -1;
}

If any other error/exception happens in the middle, it will throws an exception.

If you follows the called methods, you can see that other errors are not handled. Also, you will notice that the SQLite will be in fact be executed by a C task (and not java). So, several non-expected errors can still happen.

like image 56
W0rmH0le Avatar answered Oct 02 '22 15:10

W0rmH0le