From 1c176a9b643f02941679e024b2f00a1fbd51ebcf Mon Sep 17 00:00:00 2001 From: Daniel Santiago Rivera Date: Thu, 19 Aug 2021 08:10:54 -0700 Subject: [PATCH] Allow PRAGMA statements that change and return a result in executeNonQuery() This fixes an issue where execPerConnectionSQL() would fail with certain PRAGMA statements that both set a value and return a result, such as busy_timeout, max_page_count, etc. Bug: 197128690 Test: atest CtsDatabaseTestCases Change-Id: I1904ccfd6795c38ce55800bdd074ffba083638af --- .../database/sqlite/SQLiteConnection.java | 7 +++-- .../jni/android_database_SQLiteConnection.cpp | 28 +++++++++++-------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java index 865940e796c83..01e237da2fa03 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -153,7 +153,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen int index, byte[] value); private static native void nativeResetStatementAndClearBindings( long connectionPtr, long statementPtr); - private static native void nativeExecute(long connectionPtr, long statementPtr); + private static native void nativeExecute(long connectionPtr, long statementPtr, + boolean isPragmaStmt); private static native long nativeExecuteForLong(long connectionPtr, long statementPtr); private static native String nativeExecuteForString(long connectionPtr, long statementPtr); private static native int nativeExecuteForBlobFileDescriptor( @@ -699,6 +700,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen final int cookie = mRecentOperations.beginOperation("execute", sql, bindArgs); try { + final boolean isPragmaStmt = + DatabaseUtils.getSqlStatementType(sql) == DatabaseUtils.STATEMENT_PRAGMA; final PreparedStatement statement = acquirePreparedStatement(sql); try { throwIfStatementForbidden(statement); @@ -706,7 +709,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen applyBlockGuardPolicy(statement); attachCancellationSignal(cancellationSignal); try { - nativeExecute(mConnectionPtr, statement.mStatementPtr); + nativeExecute(mConnectionPtr, statement.mStatementPtr, isPragmaStmt); } finally { detachCancellationSignal(cancellationSignal); } diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp index c80f1dc28595e..32697aeaa47ff 100644 --- a/core/jni/android_database_SQLiteConnection.cpp +++ b/core/jni/android_database_SQLiteConnection.cpp @@ -518,23 +518,29 @@ static void nativeResetStatementAndClearBindings(JNIEnv* env, jclass clazz, jlon } } -static int executeNonQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement) { - int err = sqlite3_step(statement); - if (err == SQLITE_ROW) { +static int executeNonQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement, + bool isPragmaStmt) { + int rc = sqlite3_step(statement); + if (isPragmaStmt) { + while (rc == SQLITE_ROW) { + rc = sqlite3_step(statement); + } + } + if (rc == SQLITE_ROW) { throw_sqlite3_exception(env, "Queries can be performed using SQLiteDatabase query or rawQuery methods only."); - } else if (err != SQLITE_DONE) { + } else if (rc != SQLITE_DONE) { throw_sqlite3_exception(env, connection->db); } - return err; + return rc; } -static void nativeExecute(JNIEnv* env, jclass clazz, jlong connectionPtr, - jlong statementPtr) { +static void nativeExecute(JNIEnv* env, jclass clazz, jlong connectionPtr, jlong statementPtr, + jboolean isPragmaStmt) { SQLiteConnection* connection = reinterpret_cast(connectionPtr); sqlite3_stmt* statement = reinterpret_cast(statementPtr); - executeNonQuery(env, connection, statement); + executeNonQuery(env, connection, statement, isPragmaStmt); } static jint nativeExecuteForChangedRowCount(JNIEnv* env, jclass clazz, @@ -542,7 +548,7 @@ static jint nativeExecuteForChangedRowCount(JNIEnv* env, jclass clazz, SQLiteConnection* connection = reinterpret_cast(connectionPtr); sqlite3_stmt* statement = reinterpret_cast(statementPtr); - int err = executeNonQuery(env, connection, statement); + int err = executeNonQuery(env, connection, statement, false); return err == SQLITE_DONE ? sqlite3_changes(connection->db) : -1; } @@ -551,7 +557,7 @@ static jlong nativeExecuteForLastInsertedRowId(JNIEnv* env, jclass clazz, SQLiteConnection* connection = reinterpret_cast(connectionPtr); sqlite3_stmt* statement = reinterpret_cast(statementPtr); - int err = executeNonQuery(env, connection, statement); + int err = executeNonQuery(env, connection, statement, false); return err == SQLITE_DONE && sqlite3_changes(connection->db) > 0 ? sqlite3_last_insert_rowid(connection->db) : -1; } @@ -912,7 +918,7 @@ static const JNINativeMethod sMethods[] = (void*)nativeBindBlob }, { "nativeResetStatementAndClearBindings", "(JJ)V", (void*)nativeResetStatementAndClearBindings }, - { "nativeExecute", "(JJ)V", + { "nativeExecute", "(JJZ)V", (void*)nativeExecute }, { "nativeExecuteForLong", "(JJ)J", (void*)nativeExecuteForLong },