diff options
6 files changed, 183 insertions, 58 deletions
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java index 382e4f8b04e9..b7489229a424 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -176,6 +176,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen private static native void nativeCancel(long connectionPtr); private static native void nativeResetCancel(long connectionPtr, boolean cancelable); private static native int nativeLastInsertRowId(long connectionPtr); + private static native long nativeChanges(long connectionPtr); + private static native long nativeTotalChanges(long connectionPtr); private SQLiteConnection(SQLiteConnectionPool pool, SQLiteDatabaseConfiguration configuration, @@ -1823,11 +1825,36 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen * @return The ROWID of the last row to be inserted under this connection. * @hide */ - long lastInsertRowId() { + long getLastInsertRowId() { try { return nativeLastInsertRowId(mConnectionPtr); } finally { Reference.reachabilityFence(this); } } + + /** + * Return the number of database changes on the current connection made by the last SQL + * statement + * @hide + */ + long getLastChangedRowsCount() { + try { + return nativeChanges(mConnectionPtr); + } finally { + Reference.reachabilityFence(this); + } + } + + /** + * Return the total number of database changes made on the current connection. + * @hide + */ + long getTotalChangedRowsCount() { + try { + return nativeTotalChanges(mConnectionPtr); + } finally { + Reference.reachabilityFence(this); + } + } } diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index eceec34ba0f4..a3f838347258 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -2197,7 +2197,49 @@ public final class SQLiteDatabase extends SQLiteClosable { * @throws IllegalStateException if there is no current transaction. */ public long getLastInsertRowId() { - return getThreadSession().lastInsertRowId(); + return getThreadSession().getLastInsertRowId(); + } + + /** + * Return the number of database rows that were inserted, updated, or deleted by the most recent + * SQL statement within the current transaction. + * + * @see <a href="https://sqlite.org/c3ref/changes.html">sqlite3_changes64</a> + * + * @return The number of rows changed by the most recent sql statement + * @throws IllegalStateException if there is no current transaction. + * @hide + */ + public long getLastChangedRowsCount() { + return getThreadSession().getLastChangedRowsCount(); + } + + /** + * Return the total number of database rows that have been inserted, updated, or deleted on + * the current connection since it was created. Due to Android's internal management of + * SQLite connections, the value may, or may not, include changes made in earlier + * transactions. Best practice is to compare values returned within a single transaction. + * + * <code><pre> + * database.beginTransaction(); + * try { + * long initialValue = database.getTotalChangedRowsCount(); + * // Execute SQL statements + * long changedRows = database.getTotalChangedRowsCount() - initialValue; + * // changedRows counts the total number of rows updated in the transaction. + * } finally { + * database.endTransaction(); + * } + * </pre></code> + * + * @see <a href="https://sqlite.org/c3ref/changes.html">sqlite3_total_changes64</a> + * + * @return The number of rows changed on the current connection. + * @throws IllegalStateException if there is no current transaction. + * @hide + */ + public long getTotalChangedRowsCount() { + return getThreadSession().getTotalChangedRowsCount(); } /** diff --git a/core/java/android/database/sqlite/SQLiteSession.java b/core/java/android/database/sqlite/SQLiteSession.java index 2379c849e534..ef1a9cbe0adf 100644 --- a/core/java/android/database/sqlite/SQLiteSession.java +++ b/core/java/android/database/sqlite/SQLiteSession.java @@ -988,9 +988,29 @@ public final class SQLiteSession { * necessary to acquire and release the connection: the connection has already been acquired. * @hide */ - long lastInsertRowId() { + long getLastInsertRowId() { throwIfNoTransaction(); - return mConnection.lastInsertRowId(); + return mConnection.getLastInsertRowId(); + } + + /** + * Return the number of database rows that were changed by the most recent SQL statement on + * this connection. + * @hide + */ + long getLastChangedRowsCount() { + throwIfNoTransaction(); + return mConnection.getLastChangedRowsCount(); + } + + /** + * Return the total number of database rows that were changed on the current connection, since + * it was created. + * @hide + */ + long getTotalChangedRowsCount() { + throwIfNoTransaction(); + return mConnection.getTotalChangedRowsCount(); } /** diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp index 7e827a837dce..29520c24da75 100644 --- a/core/jni/android_database_SQLiteConnection.cpp +++ b/core/jni/android_database_SQLiteConnection.cpp @@ -885,6 +885,16 @@ static jint nativeLastInsertRowId(JNIEnv* env, jclass, jlong connectionPtr) { return sqlite3_last_insert_rowid(connection->db); } +static jlong nativeChanges(JNIEnv* env, jclass, jlong connectionPtr) { + SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr); + return sqlite3_changes64(connection->db); +} + +static jlong nativeTotalChanges(JNIEnv* env, jclass, jlong connectionPtr) { + SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr); + return sqlite3_total_changes64(connection->db); +} + static const JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ @@ -943,7 +953,9 @@ static const JNINativeMethod sMethods[] = { "nativeResetCancel", "(JZ)V", (void*)nativeResetCancel }, - { "nativeLastInsertRowId", "(J)I", (void*) nativeLastInsertRowId } + { "nativeLastInsertRowId", "(J)I", (void*) nativeLastInsertRowId }, + { "nativeChanges", "(J)J", (void*) nativeChanges }, + { "nativeTotalChanges", "(J)J", (void*) nativeTotalChanges }, }; int register_android_database_SQLiteConnection(JNIEnv *env) diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java index ea5e7b639731..fc72f611ccc8 100644 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java @@ -137,4 +137,81 @@ public class SQLiteDatabaseTest { fail("Timed out"); } } + + /** + * Create a database with one table with three columns. + */ + private void createComplexDatabase() { + mDatabase.beginTransaction(); + try { + mDatabase.execSQL("CREATE TABLE t1 (i int, d double, t text);"); + mDatabase.setTransactionSuccessful(); + } finally { + mDatabase.endTransaction(); + } + } + + /** + * A three-value insert for the complex database. + */ + private String createComplexInsert() { + return "INSERT INTO t1 (i, d, t) VALUES (?1, ?2, ?3)"; + } + + @Test + public void testAutomaticCounters() { + final int size = 10; + + createComplexDatabase(); + + // Put 10 lines in the database. + mDatabase.beginTransaction(); + try { + try (SQLiteRawStatement s = mDatabase.createRawStatement(createComplexInsert())) { + for (int i = 0; i < size; i++) { + int vi = i * 3; + double vd = i * 2.5; + String vt = String.format("text%02dvalue", i); + s.bindInt(1, vi); + s.bindDouble(2, vd); + s.bindText(3, vt); + boolean r = s.step(); + // No row is returned by this query. + assertFalse(r); + s.reset(); + assertEquals(i + 1, mDatabase.getLastInsertRowId()); + assertEquals(1, mDatabase.getLastChangedRowsCount()); + assertEquals(i + 2, mDatabase.getTotalChangedRowsCount()); + } + } + mDatabase.setTransactionSuccessful(); + } finally { + mDatabase.endTransaction(); + } + + // Put a second 10 lines in the database. + mDatabase.beginTransaction(); + try { + try (SQLiteRawStatement s = mDatabase.createRawStatement(createComplexInsert())) { + for (int i = 0; i < size; i++) { + int vi = i * 3; + double vd = i * 2.5; + String vt = String.format("text%02dvalue", i); + s.bindInt(1, vi); + s.bindDouble(2, vd); + s.bindText(3, vt); + boolean r = s.step(); + // No row is returned by this query. + assertFalse(r); + s.reset(); + assertEquals(size + i + 1, mDatabase.getLastInsertRowId()); + assertEquals(1, mDatabase.getLastChangedRowsCount()); + assertEquals(size + i + 2, mDatabase.getTotalChangedRowsCount()); + } + } + mDatabase.setTransactionSuccessful(); + } finally { + mDatabase.endTransaction(); + } + } } diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java index 59a1643c061f..36bb8e5496c9 100644 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java @@ -846,59 +846,6 @@ public class SQLiteRawStatementTest { } @Test - public void testLastInsertRowId() { - final int size = 10; - - createComplexDatabase(); - - // Put 10 lines in the database. - mDatabase.beginTransaction(); - try { - try (SQLiteRawStatement s = mDatabase.createRawStatement(createComplexInsert())) { - for (int i = 0; i < size; i++) { - int vi = i * 3; - double vd = i * 2.5; - String vt = String.format("text%02dvalue", i); - s.bindInt(1, vi); - s.bindDouble(2, vd); - s.bindText(3, vt); - boolean r = s.step(); - // No row is returned by this query. - assertFalse(r); - s.reset(); - assertEquals(i + 1, mDatabase.getLastInsertRowId()); - } - } - mDatabase.setTransactionSuccessful(); - } finally { - mDatabase.endTransaction(); - } - - // Put a second 10 lines in the database. - mDatabase.beginTransaction(); - try { - try (SQLiteRawStatement s = mDatabase.createRawStatement(createComplexInsert())) { - for (int i = 0; i < size; i++) { - int vi = i * 3; - double vd = i * 2.5; - String vt = String.format("text%02dvalue", i); - s.bindInt(1, vi); - s.bindDouble(2, vd); - s.bindText(3, vt); - boolean r = s.step(); - // No row is returned by this query. - assertFalse(r); - s.reset(); - assertEquals(size + i + 1, mDatabase.getLastInsertRowId()); - } - } - mDatabase.setTransactionSuccessful(); - } finally { - mDatabase.endTransaction(); - } - } - - @Test public void testUnicode() { // Create the t1 table and put some data in it. mDatabase.beginTransaction(); |