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

Commit 3a94cb5d authored by Lee Shombert's avatar Lee Shombert
Browse files

New SQLite APIs

Bug: 279043253

Introducing new and improved SQLite APIs that more closely conform to
the native methods.  The new SQLiteStmt class is a thin wrapper over
the SQLite sqlite3_stmt object.

All new APIs are currently marked @hide.  The APIs will be exposed to
the public in a separate review.  Unit tests have been added; CTS
tests will be added when the APIs are made public.

Test: atest
 * SQLiteRawStatementTest
 * SQLiteDatabaseTest

Change-Id: I4f4b09a77d75adfb6d8eb78e54989f5dc4701472
parent 14b8e32f
Loading
Loading
Loading
Loading
+55 −4
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.database.sqlite;

import android.annotation.NonNull;

import android.database.Cursor;
import android.database.CursorWindow;
import android.database.DatabaseUtils;
@@ -35,6 +37,7 @@ import dalvik.system.BlockGuard;
import dalvik.system.CloseGuard;
import java.io.File;
import java.io.IOException;
import java.lang.ref.Reference;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -167,6 +170,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
    private static native int nativeGetDbLookaside(long connectionPtr);
    private static native void nativeCancel(long connectionPtr);
    private static native void nativeResetCancel(long connectionPtr, boolean cancelable);
    private static native int nativeLastInsertRowId(long connectionPtr);

    private SQLiteConnection(SQLiteConnectionPool pool,
            SQLiteDatabaseConfiguration configuration,
@@ -1052,7 +1056,10 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
        }
    }

    private PreparedStatement acquirePreparedStatement(String sql) {
    /**
     * Return a {@link #PreparedStatement}, possibly from the cache.
     */
    PreparedStatement acquirePreparedStatement(String sql) {
        ++mPool.mTotalPrepareStatements;
        PreparedStatement statement = mPreparedStatementCache.get(sql);
        boolean skipCache = false;
@@ -1088,7 +1095,10 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
        return statement;
    }

    private void releasePreparedStatement(PreparedStatement statement) {
    /**
     * Release a {@link #PreparedStatement} that was originally supplied by this connection.
     */
    void releasePreparedStatement(PreparedStatement statement) {
        statement.mInUse = false;
        if (statement.mInCache) {
            try {
@@ -1116,6 +1126,24 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
        recyclePreparedStatement(statement);
    }

    /**
     * Return a prepared statement for use by {@link SQLiteRawStatement}.  This throws if the
     * prepared statement is incompatible with this connection.
     */
    PreparedStatement acquirePersistentStatement(@NonNull String sql) {
        final int cookie = mRecentOperations.beginOperation("prepare", sql, null);
        try {
            final PreparedStatement statement = acquirePreparedStatement(sql);
            throwIfStatementForbidden(statement);
            return statement;
        } catch (RuntimeException e) {
            mRecentOperations.failOperation(cookie, e);
            throw e;
        } finally {
            mRecentOperations.endOperation(cookie);
        }
    }

    private void attachCancellationSignal(CancellationSignal cancellationSignal) {
        if (cancellationSignal != null) {
            cancellationSignal.throwIfCanceled();
@@ -1200,7 +1228,14 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
        }
    }

    private void throwIfStatementForbidden(PreparedStatement statement) {
    /**
     * Verify that the statement is read-only, if the connection only allows read-only
     * operations.
     * @param statement The statement to check.
     * @throws SQLiteException if the statement could update the database inside a read-only
     * transaction.
     */
    void throwIfStatementForbidden(PreparedStatement statement) {
        if (mOnlyAllowReadOnlyOperations && !statement.mReadOnly) {
            throw new SQLiteException("Cannot execute this statement because it "
                    + "might modify the database but the connection is read-only.");
@@ -1401,8 +1436,10 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
     * In particular, closing the connection requires a guarantee of deterministic
     * resource disposal because all native statement objects must be freed before
     * the native database object can be closed.  So no finalizers here.
     *
     * The class is package-visible so that {@link SQLiteRawStatement} can use it.
     */
    private static final class PreparedStatement {
    static final class PreparedStatement {
        // Next item in pool.
        public PreparedStatement mPoolNext;

@@ -1742,4 +1779,18 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
        }

    }

    /**
     * Return the ROWID of the last row to be inserted under this connection.  Returns 0 if there
     * has never been an insert on this connection.
     * @return The ROWID of the last row to be inserted under this connection.
     * @hide
     */
    long lastInsertRowId() {
        try {
            return nativeLastInsertRowId(mConnectionPtr);
        } finally {
            Reference.reachabilityFence(this);
        }
    }
}
+25 −0
Original line number Diff line number Diff line
@@ -2165,6 +2165,31 @@ public final class SQLiteDatabase extends SQLiteClosable {
        }
    }

    /**
     * Return a {@link SQLiteRawStatement} connected to the database.  A transaction must be in
     * progress or an exception will be thrown.  The resulting object will be closed automatically
     * when the current transaction closes.
     * @param sql The SQL string to be compiled into a prepared statement.
     * @return A raw statement holding the compiled sql.
     * @throws IllegalStateException if a transaction is not in progress.
     * @throws SQLiteException if the sql cannot be compiled.
     * @hide
     */
    public SQLiteRawStatement createRawStatement(@NonNull String sql) {
        return new SQLiteRawStatement(this, sql);
    }

    /**
     * Return the "rowid" of the last row to be inserted on the current connection.  See the
     * SQLite documentation for the specific details.  This method must only be called when inside
     * a transaction.  {@link IllegalStateException} is thrown if the method is called outside a
     * transaction.
     * @hide
     */
    public long lastInsertRowId() {
        return getThreadSession().lastInsertRowId();
    }

    /**
     * Verifies that a SQL SELECT statement is valid by compiling it.
     * If the SQL statement is not valid, this method will throw a {@link SQLiteException}.
Loading