summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Lee Shombert <shombert@google.com> 2023-07-06 16:24:16 -0700
committer Lee Shombert <shombert@google.com> 2023-07-17 08:42:21 -0700
commit981926c6d89f4042c9b5cf8086c2d4331899c814 (patch)
tree36281f8ab88d640de8773e64d9bf09c4b3cdb4ea
parent9521b73f5057fa3a77f5c7f9a2c2e48043a53601 (diff)
Add sqlite3 changes APIs
Bug: 289226544 Add support for sqlite3_changes64() and sqlite3_total_changes64(). The APIs will be made public in a later commit. Unit tests have been added for the APIs. The unit tests are incorporated into the unit test for getLastInsertRowid(), and the entire test has been moved from SQLiteRawStatementTest to SQLiteDatabaseTest. As a code clean-up, the pipeline of "lastInsertRowId()" methods have been renamed to "getLastInsertRowId()", to match the API council's decision on the public API. Test: atest * FrameworksCoreTests:android.database.sqlite Change-Id: I1c167d6d6b8701fae0fd4129e1ae9029556c6d26
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java29
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java44
-rw-r--r--core/java/android/database/sqlite/SQLiteSession.java24
-rw-r--r--core/jni/android_database_SQLiteConnection.cpp14
-rw-r--r--core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java77
-rw-r--r--core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java53
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();