summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Lee Shombert <shombert@google.com> 2024-12-10 22:44:59 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-12-10 22:44:59 +0000
commit4acfb8f36066345744fbc065d9d1018ece8b15cb (patch)
treebf7b7b306e7b3608be4fdc91e15b1caac8e51fa7
parent0959be77a3eea5ba133e3f67b2e2517a3f4f1e00 (diff)
parentdee229be307d1847b9c6ca0534b820b7db8ee2eb (diff)
Merge "Correct check on SQLiteRawStatement.getColumnName" into main
-rw-r--r--core/java/android/database/sqlite/SQLiteRawStatement.java4
-rw-r--r--core/jni/android_database_SQLiteRawStatement.cpp29
-rw-r--r--core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java23
3 files changed, 49 insertions, 7 deletions
diff --git a/core/java/android/database/sqlite/SQLiteRawStatement.java b/core/java/android/database/sqlite/SQLiteRawStatement.java
index 3f3e46b4334c..ce2334a8247a 100644
--- a/core/java/android/database/sqlite/SQLiteRawStatement.java
+++ b/core/java/android/database/sqlite/SQLiteRawStatement.java
@@ -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() {
diff --git a/core/jni/android_database_SQLiteRawStatement.cpp b/core/jni/android_database_SQLiteRawStatement.cpp
index 32c2ef73a5b1..5fa808361178 100644
--- a/core/jni/android_database_SQLiteRawStatement.cpp
+++ b/core/jni/android_database_SQLiteRawStatement.cpp
@@ -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));
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
index 13b12fcf300a..51a43ac01d75 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
@@ -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();
+ }
+
}
}