Merge "Add sqlite3 changes APIs" into main

This commit is contained in:
Lee Shombert
2023-07-17 17:25:37 +00:00
committed by Android (Google) Code Review
6 changed files with 183 additions and 58 deletions

View File

@@ -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);
}
}
}

View File

@@ -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();
}
/**

View File

@@ -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();
}
/**

View File

@@ -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)

View File

@@ -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();
}
}
}

View File

@@ -845,59 +845,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.