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

Commit dee229be authored by Lee Shombert's avatar Lee Shombert
Browse files

Correct check on SQLiteRawStatement.getColumnName

The function sqlite3_column_name() can be called on any prepared
statement; it is not a function of the data row that may have been
retrieved.  Therefore, the bounds check for getColumnName() should use
sqlite3_column_count() and not sqlite3_data_count() (which requires a
row of data in the prepared statement).

Two test cases have been added.

Flag: EXEMPT bug-fix
Bug: 359676342
Test: atest
 * FrameworksCoreTests:android.database
 * CtsDatabaseTestCases
Change-Id: Id3908af9a43c1580ecaa3cf2b85f8c2e50204489
parent bfb0a089
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -533,11 +533,11 @@ public final class SQLiteRawStatement implements Closeable {
    }

    /**
     * Return the number of columns in the current result row.
     * Return the number of columns in the result set for the statement.
     *
     * @see <a href="http://sqlite.org/c3ref/column_count.html">sqlite3_column_count</a>
     *
     * @return The number of columns in the result row.
     * @return The number of columns in the result set.
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     */
    public int getResultColumnCount() {
+24 −5
Original line number Diff line number Diff line
@@ -81,10 +81,11 @@ static bool throwIfError(JNIEnv *env, jlong stmtPtr) {
    return true;
}

// 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.
// This throws a SQLiteBindOrColumnIndexOutOfRangeException if the column index is outside the
// bounds of the row data set.  It throws SQLiteMisuseException if the statement's data 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) {
    int count = sqlite3_data_count(stmt(stmtPtr));
    if (throwIfError(env, stmtPtr)) {
@@ -106,6 +107,24 @@ static bool throwIfInvalidColumn(JNIEnv *env, jlong stmtPtr, jint col) {
    }
}

// This throws a SQLiteBindOrColumnIndexOutOfRangeException if the column index is outside the
// bounds of the result set.  (This is not the same as the data columns in a row).  The function
// returns true if an exception was thrown.
static bool throwIfInvalidResultColumn(JNIEnv *env, jlong stmtPtr, jint col) {
    int count = sqlite3_column_count(stmt(stmtPtr));
    if (throwIfError(env, stmtPtr)) {
        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);
        throw_sqlite3_exception(env, SQLITE_RANGE, errmsg, message.c_str());
        return true;
    } else {
        return false;
    }
}

static jint bindParameterCount(JNIEnv* env, jclass, jlong stmtPtr) {
    return sqlite3_bind_parameter_count(stmt(stmtPtr));
}
@@ -235,7 +254,7 @@ static jint columnType(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
}

static jstring columnName(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
    if (throwIfInvalidColumn(env, stmtPtr, col)) {
    if (throwIfInvalidResultColumn(env, stmtPtr, col)) {
        return nullptr;
    }
    const jchar* name = static_cast<const jchar*>(sqlite3_column_name16(stmt(stmtPtr), col));
+23 −0
Original line number Diff line number Diff line
@@ -1048,5 +1048,28 @@ public class SQLiteRawStatementTest {
        } finally {
            mDatabase.endTransaction();
        }

        // Ensure that column names and column types can be fetched even if the statement is not
        // stepped.  A new SQL statement is created to avoid interaction from the statement cache.
        mDatabase.beginTransactionReadOnly();
        try (SQLiteRawStatement s = mDatabase.createRawStatement("SELECT * from t1 WHERE j = 3")) {
            // Do not step the statement.
            assertEquals("i", s.getColumnName(0));
            assertEquals("j", s.getColumnName(1));
        } finally {
            mDatabase.endTransaction();
        }

        mDatabase.beginTransactionReadOnly();
        try (SQLiteRawStatement s = mDatabase.createRawStatement("SELECT * from t1")) {
            // Do not step the statement.
            s.getColumnName(3); // out-of-range column
            fail("JNI exception not thrown");
        } catch (SQLiteBindOrColumnIndexOutOfRangeException e) {
            // Passing case.
        } finally {
            mDatabase.endTransaction();
        }

    }
}