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

Commit 4e8dff1f authored by Lee Shombert's avatar Lee Shombert Committed by Android (Google) Code Review
Browse files

Merge "New SQLite APIs"

parents 8a257f71 3a94cb5d
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}.
+741 −0

File added.

Preview size limit exceeded, changes collapsed.

+80 −1
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.database.sqlite;

import android.annotation.NonNull;

import android.compat.annotation.UnsupportedAppUsage;
import android.database.CursorWindow;
import android.database.DatabaseUtils;
@@ -23,6 +25,10 @@ import android.os.CancellationSignal;
import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor;

import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayDeque;

/**
 * Provides a single client the ability to use a database.
 *
@@ -170,6 +176,11 @@ public final class SQLiteSession {
    private Transaction mTransactionPool;
    private Transaction mTransactionStack;

    /**
     * A list of dependents that should be closed when the transaction completes.
     */
    private final ArrayDeque<Closeable> mOpenDependents = new ArrayDeque<>();

    /**
     * Transaction mode: Deferred.
     * <p>
@@ -379,6 +390,9 @@ public final class SQLiteSession {
        throwIfTransactionMarkedSuccessful();

        mTransactionStack.mMarkedSuccessful = true;
        // Close open dependents, since the next thing that is supposed to happen is the transaction
        // ends.
        closeOpenDependents();
    }

    /**
@@ -439,6 +453,11 @@ public final class SQLiteSession {
                mTransactionStack.mChildFailed = true;
            }
        } else {
            // Close all dependents before anything that might throw.  The list should have been
            // cleared when the transaction was marked successful or unsuccessful.  The call here
            // does nothing if the list is empty but is provided for insurance.
            closeOpenDependents();

            try {
                if (successful) {
                    mConnection.execute("COMMIT;", null, cancellationSignal); // might throw
@@ -917,7 +936,67 @@ public final class SQLiteSession {
        }
    }

    private void throwIfNoTransaction() {
    /**
     * Acquire a prepared statement for external use. A current transaction is required and that
     * transaction may not have been marked successful. The dependent is registered its close()
     * method is called when the transaction is closed.
     */
    @NonNull
    SQLiteConnection.PreparedStatement acquirePersistentStatement(@NonNull String query,
            @NonNull Closeable dependent) {
        throwIfNoTransaction();
        throwIfTransactionMarkedSuccessful();
        mOpenDependents.addFirst(dependent);
        try {
            return mConnection.acquirePersistentStatement(query);
        } catch (Throwable e) {
            mOpenDependents.remove(dependent);
            throw e;
        }
    }

    /**
     * Release a prepared statement.  The dependent should be in list of open dependents.
     */
    void releasePersistentStatement(@NonNull SQLiteConnection.PreparedStatement statement,
            @NonNull Closeable dependent) {
        mConnection.releasePreparedStatement(statement);
        mOpenDependents.remove(dependent);
    }

    /**
     * Close any open dependents.  This may be called multiple times without harm.  It never
     * throws.
     */
    void closeOpenDependents() {
        while (mOpenDependents.size() > 0) {
            final Closeable dependent = mOpenDependents.pollFirst();
            if (dependent != null)
                try {
                    dependent.close();
                } catch (IOException e) {
                    // Swallow the exception.
                }
        }
    }

    /**
     * Return the row ID of the last row to be inserted on this connection.  Note that the last row
     * might not have been inserted on this particular statement, but the return value is the last
     * row inserted on the same connection as that used by this statement.  The function checks that
     * it is currently in a transaction before executing.  Because of this check, it is not
     * necessary to acquire and release the connection: the connection has already been acquired.
     * @hide
     */
    long lastInsertRowId() {
        throwIfNoTransaction();
        return mConnection.lastInsertRowId();
    }

    /**
     * Like it says on the tin: throw if there is no current transaction.
     */
    void throwIfNoTransaction() {
        if (mTransactionStack == null) {
            throw new IllegalStateException("Cannot perform this operation because "
                    + "there is no current transaction.");
+1 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.os.ParcelFileDescriptor;
 * <p>
 * This class is not thread-safe.
 * </p>
 * Note that this class is unrelated to {@link SQLiteRawStatement}.
 */
public final class SQLiteStatement extends SQLiteProgram {
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Loading