Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 981926c6 authored by Lee Shombert's avatar Lee Shombert
Browse files

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
parent 9521b73f
Loading
Loading
Loading
Loading
+28 −1
Original line number Diff line number Diff line
@@ -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);
        }
    }
}
+43 −1
Original line number Diff line number Diff line
@@ -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();
    }

    /**
+22 −2
Original line number Diff line number Diff line
@@ -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();
    }

    /**
+13 −1
Original line number Diff line number Diff line
@@ -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)
+77 −0
Original line number Diff line number Diff line
@@ -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();
        }
    }
}
Loading