Loading core/java/android/database/sqlite/SQLiteRawStatement.java +14 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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) { Loading @@ -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 Loading Loading @@ -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, Loading Loading @@ -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) { Loading @@ -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) { Loading @@ -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) { Loading @@ -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 Loading core/jni/android_database_SQLiteRawStatement.cpp +24 −15 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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)); } Loading core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java +25 −2 Original line number Diff line number Diff line Loading @@ -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(); Loading @@ -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(); } } } Loading
core/java/android/database/sqlite/SQLiteRawStatement.java +14 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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) { Loading @@ -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 Loading Loading @@ -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, Loading Loading @@ -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) { Loading @@ -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) { Loading @@ -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) { Loading @@ -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 Loading
core/jni/android_database_SQLiteRawStatement.cpp +24 −15 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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)); } Loading
core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java +25 −2 Original line number Diff line number Diff line Loading @@ -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(); Loading @@ -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(); } } }