summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Lee Shombert <shombert@google.com> 2023-07-17 17:25:37 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2023-07-17 17:25:37 +0000
commitaec33556e176f6eee11ec0b65c129e9901f2baca (patch)
treefad73393e2f63d400359d313a701531e3760019f
parent6df6883ad7b67e53bceea6b19fa6dbe59e77df76 (diff)
parent981926c6d89f4042c9b5cf8086c2d4331899c814 (diff)
Merge "Add sqlite3 changes APIs" into main
-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();