Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0b3b6a65 authored by Lee Shombert's avatar Lee Shombert Committed by Android (Google) Code Review
Browse files

Merge "Fine-tune sqlite misuse exceptions" into main

parents 69034085 4cc641f5
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -554,10 +554,16 @@ public final class SQLiteRawStatement implements Closeable {
     *
     * @see <a href="http://sqlite.org/c3ref/column_blob.html">sqlite3_column_type</a>
     *
     * If the row has no data then a {@link SQLiteMisuseException} is thrown.  This condition can
     * occur the last call to {@link #step()} returned false or if {@link #step()} was not called
     * before the statement was created or after the last call to {@link #reset()}.  Note that
     * {@link SQLiteMisuseException} may be thrown for other reasons.
     *
     * @param columnIndex The index of a column in the result row. It is zero-based.
     * @return The type of the value in the column of the result row.
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
     * @throws SQLiteMisuseException if the row has no data.
     * @throws SQLiteException if a native error occurs.
     */
    @SQLiteDataType
@@ -580,6 +586,7 @@ public final class SQLiteRawStatement implements Closeable {
     * @return The name of the column in the result row.
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
     * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
     * @throws SQLiteOutOfMemoryException if the database cannot allocate memory for the name.
     */
    @NonNull
@@ -606,6 +613,7 @@ public final class SQLiteRawStatement implements Closeable {
     * @return The length, in bytes, of the value in the column.
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
     * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
     * @throws SQLiteException if a native error occurs.
     */
    public int getColumnLength(int columnIndex) {
@@ -631,6 +639,7 @@ public final class SQLiteRawStatement implements Closeable {
     * @return The value of the column as a blob, or null if the column is NULL.
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
     * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
     * @throws SQLiteException if a native error occurs.
     */
    @Nullable
@@ -664,6 +673,7 @@ public final class SQLiteRawStatement implements Closeable {
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     * @throws IllegalArgumentException if the buffer is too small for offset+length.
     * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
     * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
     * @throws SQLiteException if a native error occurs.
     */
    public int readColumnBlob(int columnIndex, @NonNull byte[] buffer, int offset,
@@ -691,6 +701,7 @@ public final class SQLiteRawStatement implements Closeable {
     * @return The value of a column as a double.
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
     * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
     * @throws SQLiteException if a native error occurs.
     */
    public double getColumnDouble(int columnIndex) {
@@ -715,6 +726,7 @@ public final class SQLiteRawStatement implements Closeable {
     * @return The value of the column as an int.
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
     * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
     * @throws SQLiteException if a native error occurs.
     */
    public int getColumnInt(int columnIndex) {
@@ -739,6 +751,7 @@ public final class SQLiteRawStatement implements Closeable {
     * @return The value of the column as an long.
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
     * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
     * @throws SQLiteException if a native error occurs.
     */
    public long getColumnLong(int columnIndex) {
@@ -763,6 +776,7 @@ public final class SQLiteRawStatement implements Closeable {
     * @return The value of the column as a string.
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
     * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
     * @throws SQLiteException if a native error occurs.
     */
    @NonNull
+24 −15
Original line number Diff line number Diff line
@@ -70,12 +70,32 @@ static void throwInvalidParameter(JNIEnv *env, jlong stmtPtr, jint index) {
    }
}

// If the last operation failed, throw an exception and return true.  Otherwise return false.
static bool throwIfError(JNIEnv *env, jlong stmtPtr) {
    switch (sqlite3_errcode(db(stmtPtr))) {
        case SQLITE_OK:
        case SQLITE_DONE:
        case SQLITE_ROW: return false;
    }
    throw_sqlite3_exception(env, db(stmtPtr), nullptr);
    return true;
}

// This throws a SQLiteBindOrColumnIndexOutOfRangeException if the column index is out
// of bounds.  It returns true if an exception was thrown.
// This throws a SQLiteBindOrColumnIndexOutOfRangeException if the column index is out of
// bounds.  It throws SQLiteMisuseException if the statement's column count is zero; that
// generally occurs because the client has forgotten to call step() or the client has stepped
// past the end of the query.  The function returns true if an exception was thrown.
static bool throwIfInvalidColumn(JNIEnv *env, jlong stmtPtr, jint col) {
    if (col < 0 || col >= sqlite3_data_count(stmt(stmtPtr))) {
    int count = sqlite3_data_count(stmt(stmtPtr));
    if (throwIfError(env, stmtPtr)) {
        return true;
    } else if (count == 0) {
        // A count of zero indicates a misuse: the statement has never been step()'ed.
        const char* message = "row has no data";
        const char* errmsg = sqlite3_errstr(SQLITE_MISUSE);
        throw_sqlite3_exception(env, SQLITE_MISUSE, errmsg, message);
        return true;
    } else if (col < 0 || col >= count) {
        std::string message = android::base::StringPrintf(
            "column index %d out of bounds [0,%d]", col, count - 1);
        char const * errmsg = sqlite3_errstr(SQLITE_RANGE);
@@ -86,17 +106,6 @@ static bool throwIfInvalidColumn(JNIEnv *env, jlong stmtPtr, jint col) {
    }
}

// If the last operation failed, throw an exception and return true.  Otherwise return false.
static bool throwIfError(JNIEnv *env, jlong stmtPtr) {
    switch (sqlite3_errcode(db(stmtPtr))) {
        case SQLITE_OK:
        case SQLITE_DONE:
        case SQLITE_ROW: return false;
    }
    throw_sqlite3_exception(env, db(stmtPtr), nullptr);
    return true;
}

static jint bindParameterCount(JNIEnv* env, jclass, jlong stmtPtr) {
    return sqlite3_bind_parameter_count(stmt(stmtPtr));
}
+25 −2
Original line number Diff line number Diff line
@@ -1010,6 +1010,7 @@ public class SQLiteRawStatementTest {
        mDatabase.beginTransaction();
        try {
            mDatabase.execSQL("CREATE TABLE t1 (i int, j int);");
            mDatabase.execSQL("INSERT INTO t1 (i, j) VALUES (2, 20)");
            mDatabase.setTransactionSuccessful();
        } finally {
            mDatabase.endTransaction();
@@ -1017,13 +1018,35 @@ public class SQLiteRawStatementTest {

        mDatabase.beginTransactionReadOnly();
        try (SQLiteRawStatement s = mDatabase.createRawStatement("SELECT * from t1")) {
            s.step();
            s.getColumnText(5); // out-of-range column
            assertTrue(s.step());
            s.getColumnText(5); // out-of-range column: the range is [0,2).
            fail("JNI exception not thrown");
        } catch (SQLiteBindOrColumnIndexOutOfRangeException e) {
            // Passing case.
        } finally {
            mDatabase.endTransaction();
        }

        mDatabase.beginTransactionReadOnly();
        try (SQLiteRawStatement s = mDatabase.createRawStatement("SELECT * from t1")) {
            // Do not step the statement.  The column count will be zero.
            s.getColumnText(5); // out-of-range column: never stepped.
            fail("JNI exception not thrown");
        } catch (SQLiteMisuseException e) {
            // Passing case.
        } finally {
            mDatabase.endTransaction();
        }

        mDatabase.beginTransactionReadOnly();
        try (SQLiteRawStatement s = mDatabase.createRawStatement("SELECT * from t1")) {
            // Do not step the statement.  The column count will be zero.
            s.getColumnText(0); // out-of-range column: never stepped.
            fail("JNI exception not thrown");
        } catch (SQLiteMisuseException e) {
            // Passing case.
        } finally {
            mDatabase.endTransaction();
        }
    }
}