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

Commit a17b5f7c authored by Vasu Nori's avatar Vasu Nori Committed by Android (Google) Code Review
Browse files

Merge "do begin-end transaction before standalone insert/update/delete sql"

parents 3208c26f ce38b98f
Loading
Loading
Loading
Loading
+90 −0
Original line number Diff line number Diff line
@@ -61622,6 +61622,19 @@
<parameter name="name" type="java.lang.String">
</parameter>
</method>
<method name="getSqlStatementType"
 return="int"
 abstract="false"
 native="false"
 synchronized="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="sql" type="java.lang.String">
</parameter>
</method>
<method name="longForQuery"
 return="long"
 abstract="false"
@@ -61772,6 +61785,83 @@
<parameter name="e" type="java.lang.Exception">
</parameter>
</method>
<field name="STATEMENT_ABORT"
 type="int"
 transient="false"
 volatile="false"
 value="6"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="STATEMENT_ATTACH"
 type="int"
 transient="false"
 volatile="false"
 value="3"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="STATEMENT_BEGIN"
 type="int"
 transient="false"
 volatile="false"
 value="4"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="STATEMENT_COMMIT"
 type="int"
 transient="false"
 volatile="false"
 value="5"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="STATEMENT_OTHER"
 type="int"
 transient="false"
 volatile="false"
 value="7"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="STATEMENT_SELECT"
 type="int"
 transient="false"
 volatile="false"
 value="1"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="STATEMENT_UPDATE"
 type="int"
 transient="false"
 volatile="false"
 value="2"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
</class>
<class name="DatabaseUtils.InsertHelper"
 extends="java.lang.Object"
+56 −0
Original line number Diff line number Diff line
@@ -52,6 +52,21 @@ public class DatabaseUtils {

    private static final String[] countProjection = new String[]{"count(*)"};

    /** One of the values returned by {@link #getSqlStatementType(String)}. */
    public static final int STATEMENT_SELECT = 1;
    /** One of the values returned by {@link #getSqlStatementType(String)}. */
    public static final int STATEMENT_UPDATE = 2;
    /** One of the values returned by {@link #getSqlStatementType(String)}. */
    public static final int STATEMENT_ATTACH = 3;
    /** One of the values returned by {@link #getSqlStatementType(String)}. */
    public static final int STATEMENT_BEGIN = 4;
    /** One of the values returned by {@link #getSqlStatementType(String)}. */
    public static final int STATEMENT_COMMIT = 5;
    /** One of the values returned by {@link #getSqlStatementType(String)}. */
    public static final int STATEMENT_ABORT = 6;
    /** One of the values returned by {@link #getSqlStatementType(String)}. */
    public static final int STATEMENT_OTHER = 7;

    /**
     * Special function for writing an exception result at the header of
     * a parcel, to be used when returning an exception from a transaction.
@@ -1159,4 +1174,45 @@ public class DatabaseUtils {
        db.setVersion(dbVersion);
        db.close();
    }

    /**
     * Returns one of the following which represent the type of the given SQL statement.
     * <ol>
     *   <li>{@link #STATEMENT_SELECT}</li>
     *   <li>{@link #STATEMENT_UPDATE}</li>
     *   <li>{@link #STATEMENT_ATTACH}</li>
     *   <li>{@link #STATEMENT_BEGIN}</li>
     *   <li>{@link #STATEMENT_COMMIT}</li>
     *   <li>{@link #STATEMENT_ABORT}</li>
     *   <li>{@link #STATEMENT_OTHER}</li>
     * </ol>
     * @param sql the SQL statement whose type is returned by this method
     * @return one of the values listed above
     */
    public static int getSqlStatementType(String sql) {
        sql = sql.trim();
        if (sql.length() < 3) {
            return STATEMENT_OTHER;
        }
        String prefixSql = sql.substring(0, 3).toUpperCase();
        if (prefixSql.equals("SEL")) {
            return STATEMENT_SELECT;
        } else if (prefixSql.equals("INS") ||
                prefixSql.equals("UPD") ||
                prefixSql.equals("REP") ||
                prefixSql.equals("DEL")) {
            return STATEMENT_UPDATE;
        } else if (prefixSql.equals("ATT")) {
            return STATEMENT_ATTACH;
        } else if (prefixSql.equals("COM")) {
            return STATEMENT_COMMIT;
        } else if (prefixSql.equals("END")) {
            return STATEMENT_COMMIT;
        } else if (prefixSql.equals("ROL")) {
            return STATEMENT_ABORT;
        } else if (prefixSql.equals("BEG")) {
            return STATEMENT_BEGIN;
        }
        return STATEMENT_OTHER;
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -63,6 +63,9 @@ import java.util.Random;
     */
    /* package */ void close() {
        synchronized(mParentDbObj) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "Closing the connection pool on " + mParentDbObj.getPath() + toString());
            }
            for (int i = mPool.size() - 1; i >= 0; i--) {
                mPool.get(i).mDb.close();
            }
+81 −65
Original line number Diff line number Diff line
@@ -192,6 +192,11 @@ public class SQLiteDatabase extends SQLiteClosable {
     */
    private SQLiteTransactionListener mTransactionListener;

    /**
     * this member is set if {@link #execSQL(String)} is used to begin and end transactions.
     */
    private boolean mTransactionUsingExecSql;

    /** Synchronize on this when accessing the database */
    private final ReentrantLock mLock = new ReentrantLock(true);

@@ -236,9 +241,6 @@ public class SQLiteDatabase extends SQLiteClosable {
    /** Used by native code, do not rename. make it volatile, so it is thread-safe. */
    /* package */ volatile int mNativeHandle = 0;

    /** Used to make temp table names unique */
    /* package */ int mTempTableSequence = 0;

    /**
     * The size, in bytes, of a block on "/data". This corresponds to the Unix
     * statfs.f_bsize field. note that this field is lazily initialized.
@@ -621,9 +623,6 @@ public class SQLiteDatabase extends SQLiteClosable {

            // This thread didn't already have the lock, so begin a database
            // transaction now.
            // STOPSHIP - uncomment the following 1 line
            // if (exclusive) {
            // STOPSHIP - remove the following 1 line
            if (exclusive && mConnectionPool == null) {
                execSQL("BEGIN EXCLUSIVE;");
            } else {
@@ -740,7 +739,50 @@ public class SQLiteDatabase extends SQLiteClosable {
     * return true if there is a transaction pending
     */
    public boolean inTransaction() {
        return mLock.getHoldCount() > 0;
        return mLock.getHoldCount() > 0 || mTransactionUsingExecSql;
    }

    /* package */ synchronized void setTransactionUsingExecSqlFlag() {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.i(TAG, "found execSQL('begin transaction')");
        }
        mTransactionUsingExecSql = true;
    }

    /* package */ synchronized void resetTransactionUsingExecSqlFlag() {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            if (mTransactionUsingExecSql) {
                Log.i(TAG, "found execSQL('commit or end or rollback')");
            }
        }
        mTransactionUsingExecSql = false;
    }

    /**
     * Returns true if the caller is considered part of the current transaction, if any.
     * <p>
     * Caller is part of the current transaction if either of the following is true
     * <ol>
     *   <li>If transaction is started by calling beginTransaction() methods AND if the caller is
     *   in the same thread as the thread that started the transaction.
     *   </li>
     *   <li>If the transaction is started by calling {@link #execSQL(String)} like this:
     *   execSQL("BEGIN transaction"). In this case, every thread in the process is considered
     *   part of the current transaction.</li>
     * </ol>
     *
     * @return true if the caller is considered part of the current transaction, if any.
     */
    /* package */ synchronized boolean amIInTransaction() {
        // always do this test on the main database connection - NOT on pooled database connection
        // since transactions always occur on the main database connections only.
        SQLiteDatabase db = (isPooledConnection()) ? mParentConnObj : this;
        boolean b = (!db.inTransaction()) ? false :
                db.mTransactionUsingExecSql || db.mLock.isHeldByCurrentThread();
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.i(TAG, "amIinTransaction: " + b);
        }
        return b;
    }

    /**
@@ -932,6 +974,9 @@ public class SQLiteDatabase extends SQLiteClosable {
            DatabaseErrorHandler errorHandler, short connectionNum) {
        SQLiteDatabase db = new SQLiteDatabase(path, factory, flags, errorHandler, connectionNum);
        try {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.i(TAG, "opening the db : " + path);
            }
            // Open the database.
            db.dbopen(path, flags);
            db.setLocale(Locale.getDefault());
@@ -1008,7 +1053,7 @@ public class SQLiteDatabase extends SQLiteClosable {
        if (!isOpen()) {
            return; // already closed
        }
        if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.i(TAG, "closing db: " + mPath + " (connection # " + mConnectionNum);
        }
        lock();
@@ -1020,6 +1065,10 @@ public class SQLiteDatabase extends SQLiteClosable {
            // close this database instance - regardless of its reference count value
            dbclose();
            if (mConnectionPool != null) {
                if (Log.isLoggable(TAG, Log.DEBUG)) {
                    assert mConnectionPool != null;
                    Log.i(TAG, mConnectionPool.toString());
                }
                mConnectionPool.close();
            }
        } finally {
@@ -1586,7 +1635,6 @@ public class SQLiteDatabase extends SQLiteClosable {
    public long insertWithOnConflict(String table, String nullColumnHack,
            ContentValues initialValues, int conflictAlgorithm) {
        verifyDbIsOpen();
        BlockGuard.getThreadPolicy().onWriteToDisk();

        // Measurements show most sql lengths <= 152
        StringBuilder sql = new StringBuilder(152);
@@ -1625,7 +1673,6 @@ public class SQLiteDatabase extends SQLiteClosable {
        sql.append(values);
        sql.append(");");

        lock();
        SQLiteStatement statement = null;
        try {
            statement = compileStatement(sql.toString());
@@ -1649,7 +1696,6 @@ public class SQLiteDatabase extends SQLiteClosable {
            if (statement != null) {
                statement.close();
            }
            unlock();
        }
    }

@@ -1665,8 +1711,6 @@ public class SQLiteDatabase extends SQLiteClosable {
     */
    public int delete(String table, String whereClause, String[] whereArgs) {
        verifyDbIsOpen();
        BlockGuard.getThreadPolicy().onWriteToDisk();
        lock();
        SQLiteStatement statement = null;
        try {
            statement = compileStatement("DELETE FROM " + table
@@ -1686,7 +1730,6 @@ public class SQLiteDatabase extends SQLiteClosable {
            if (statement != null) {
                statement.close();
            }
            unlock();
        }
    }

@@ -1717,7 +1760,6 @@ public class SQLiteDatabase extends SQLiteClosable {
     */
    public int updateWithOnConflict(String table, ContentValues values,
            String whereClause, String[] whereArgs, int conflictAlgorithm) {
        BlockGuard.getThreadPolicy().onWriteToDisk();
        if (values == null || values.size() == 0) {
            throw new IllegalArgumentException("Empty values");
        }
@@ -1746,7 +1788,6 @@ public class SQLiteDatabase extends SQLiteClosable {
        }

        verifyDbIsOpen();
        lock();
        SQLiteStatement statement = null;
        try {
            statement = compileStatement(sql.toString());
@@ -1781,7 +1822,6 @@ public class SQLiteDatabase extends SQLiteClosable {
            if (statement != null) {
                statement.close();
            }
            unlock();
        }
    }

@@ -1789,9 +1829,7 @@ public class SQLiteDatabase extends SQLiteClosable {
     * Execute a single SQL statement that is NOT a SELECT
     * or any other SQL statement that returns data.
     * <p>
     * Use of this method is discouraged as it doesn't perform well when issuing the same SQL
     * statement repeatedly (see {@link #compileStatement(String)} to prepare statements for
     * repeated use), and it has no means to return any data (such as the number of affected rows).
     * It has no means to return any data (such as the number of affected rows).
     * Instead, you're encouraged to use {@link #insert(String, String, ContentValues)},
     * {@link #update(String, ContentValues, String, String[])}, et al, when possible.
     * </p>
@@ -1807,35 +1845,17 @@ public class SQLiteDatabase extends SQLiteClosable {
     * @throws SQLException If the SQL string is invalid for some reason
     */
    public void execSQL(String sql) throws SQLException {
        sql = sql.trim();
        String prefix = sql.substring(0, 6);
        if (prefix.equalsIgnoreCase("ATTACH")) {
        int stmtType = DatabaseUtils.getSqlStatementType(sql);
        if (stmtType == DatabaseUtils.STATEMENT_ATTACH) {
            disableWriteAheadLogging();
        }
        verifyDbIsOpen();
        BlockGuard.getThreadPolicy().onWriteToDisk();
        long timeStart = SystemClock.uptimeMillis();
        lock();
        logTimeStat(mLastSqlStatement, timeStart, GET_LOCK_LOG_PREFIX);
        SQLiteStatement stmt = null;
        try {
            closePendingStatements();
            stmt = compileStatement(sql);
            stmt.execute();
        } catch (SQLiteDatabaseCorruptException e) {
            onCorruption();
            throw e;
        } finally {
            if (stmt != null) {
                stmt.close();
            }
            unlock();
        }
        executeSql(sql, null);

        // Log commit statements along with the most recently executed
        // SQL statement for disambiguation.  Note that instance
        // equality to COMMIT_SQL is safe here.
        if (sql == COMMIT_SQL) {
        // SQL statement for disambiguation.
        if (stmtType == DatabaseUtils.STATEMENT_COMMIT) {
            logTimeStat(mLastSqlStatement, timeStart, COMMIT_SQL);
        } else {
            logTimeStat(sql, timeStart, null);
@@ -1886,13 +1906,15 @@ public class SQLiteDatabase extends SQLiteClosable {
     * @throws SQLException If the SQL string is invalid for some reason
     */
    public void execSQL(String sql, Object[] bindArgs) throws SQLException {
        BlockGuard.getThreadPolicy().onWriteToDisk();
        if (bindArgs == null) {
            throw new IllegalArgumentException("Empty bindArgs");
        }
        executeSql(sql, bindArgs);
    }

    private void executeSql(String sql, Object[] bindArgs) throws SQLException {
        verifyDbIsOpen();
        long timeStart = SystemClock.uptimeMillis();
        lock();
        SQLiteStatement statement = null;
        try {
            statement = compileStatement(sql);
@@ -1902,7 +1924,7 @@ public class SQLiteDatabase extends SQLiteClosable {
                    DatabaseUtils.bindObjectToProgram(statement, i + 1, bindArgs[i]);
                }
            }
            statement.executeUpdateDelete();
            statement.execute();
        } catch (SQLiteDatabaseCorruptException e) {
            onCorruption();
            throw e;
@@ -1910,7 +1932,6 @@ public class SQLiteDatabase extends SQLiteClosable {
            if (statement != null) {
                statement.close();
            }
            unlock();
        }
        logTimeStat(sql, timeStart);
    }
@@ -2142,7 +2163,8 @@ public class SQLiteDatabase extends SQLiteClosable {
        }
    }

    private void deallocCachedSqlStatements() {
    /** package-level access for testing purposes */
    /* package */ void deallocCachedSqlStatements() {
        synchronized (mCompiledQueries) {
            for (SQLiteCompiledSql compiledSql : mCompiledQueries.values()) {
                compiledSql.releaseSqlStatement();
@@ -2220,11 +2242,7 @@ public class SQLiteDatabase extends SQLiteClosable {
        }
    }

    /**
     * public visibility only for testing. otherwise, package visibility is sufficient
     * @hide
     */
    public void closePendingStatements() {
    /* package */ void closePendingStatements() {
        if (!isOpen()) {
            // since this database is already closed, no need to finalize anything.
            mClosedStatementIds.clear();
@@ -2246,9 +2264,8 @@ public class SQLiteDatabase extends SQLiteClosable {

    /**
     * for testing only
     * @hide
     */
    public ArrayList<Integer> getQueuedUpStmtList() {
    /* package */ ArrayList<Integer> getQueuedUpStmtList() {
        return mClosedStatementIds;
    }

@@ -2303,7 +2320,10 @@ public class SQLiteDatabase extends SQLiteClosable {
        // make sure this database has NO attached databases because sqlite's write-ahead-logging
        // doesn't work for databases with attached databases
        if (getAttachedDbs().size() > 1) {
            Log.i(TAG, "this database: " + mPath + " has attached databases. can't  enable WAL.");
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG,
                        "this database: " + mPath + " has attached databases. can't  enable WAL.");
            }
            return false;
        }
        if (mConnectionPool == null) {
@@ -2331,7 +2351,8 @@ public class SQLiteDatabase extends SQLiteClosable {
    /* package */ SQLiteDatabase getDatabaseHandle(String sql) {
        if (isPooledConnection()) {
            // this is a pooled database connection
            if (isOpen()) {
            // use it if it is open AND if I am not currently part of a transaction
            if (isOpen() && !amIInTransaction()) {
                // TODO: use another connection from the pool
                // if this connection is currently in use by some other thread
                // AND if there are free connections in the pool
@@ -2394,22 +2415,17 @@ public class SQLiteDatabase extends SQLiteClosable {
        if (isPooledConnection()) {
            throw new IllegalStateException("incorrect database connection handle");
        }
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            // this method shoudl never be called with anything other than SELECT
            if (sql.substring(0, 6).equalsIgnoreCase("SELECT")) {
                throw new IllegalStateException("unexpected SQL statement: " + sql);
            }
        }

        // use the current connection handle if
        // 1. if this thread is in a transaction
        // 1. if the caller is part of the ongoing transaction, if any
        // 2. OR, if there is NO connection handle pool setup
        if ((inTransaction() && mLock.isHeldByCurrentThread()) || mConnectionPool == null) {
        if (amIInTransaction() || mConnectionPool == null) {
            return this;
        } else {
            // get a connection handle from the pool
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                assert mConnectionPool != null;
                Log.i(TAG, mConnectionPool.toString());
            }
            return mConnectionPool.get(sql);
        }
+14 −30
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.database.sqlite;

import android.database.DatabaseUtils;
import android.util.Log;
import android.util.Pair;

@@ -31,11 +32,6 @@ public abstract class SQLiteProgram extends SQLiteClosable {

    private static final String TAG = "SQLiteProgram";

    /** the type of sql statement being processed by this object */
    /* package */ static final int SELECT_STMT = 1;
    private static final int UPDATE_STMT = 2;
    private static final int OTHER_STMT = 3;

    /** The database this program is compiled against.
     * @deprecated do not use this
     */
@@ -88,7 +84,9 @@ public abstract class SQLiteProgram extends SQLiteClosable {
     * <p>
     * It is protected (in multi-threaded environment) by {@link SQLiteProgram}.this
     */
    private ArrayList<Pair<Integer, Object>> bindArgs = null;
    private ArrayList<Pair<Integer, Object>> mBindArgs = null;

    /* package */ final int mStatementType;

    /* package */ SQLiteProgram(SQLiteDatabase db, String sql) {
        this(db, sql, true);
@@ -96,6 +94,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {

    /* package */ SQLiteProgram(SQLiteDatabase db, String sql, boolean compileFlag) {
        mSql = sql.trim();
        mStatementType = DatabaseUtils.getSqlStatementType(mSql);
        db.acquireReference();
        db.addSQLiteClosable(this);
        mDatabase = db;
@@ -107,7 +106,8 @@ public abstract class SQLiteProgram extends SQLiteClosable {

    private void compileSql() {
        // only cache CRUD statements
        if (getSqlStatementType(mSql) == OTHER_STMT) {
        if (mStatementType != DatabaseUtils.STATEMENT_SELECT &&
                mStatementType != DatabaseUtils.STATEMENT_UPDATE) {
            mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
            nStatement = mCompiledSql.nStatement;
            // since it is not in the cache, no need to acquire() it.
@@ -150,22 +150,6 @@ public abstract class SQLiteProgram extends SQLiteClosable {
        nStatement = mCompiledSql.nStatement;
    }

    /* package */ int getSqlStatementType(String sql) {
        if (mSql.length() < 6) {
            return OTHER_STMT;
        }
        String prefixSql = mSql.substring(0, 6);
        if (prefixSql.equalsIgnoreCase("SELECT")) {
            return SELECT_STMT;
        } else if (prefixSql.equalsIgnoreCase("INSERT") ||
                prefixSql.equalsIgnoreCase("UPDATE") ||
                prefixSql.equalsIgnoreCase("REPLAC") ||
                prefixSql.equalsIgnoreCase("DELETE")) {
            return UPDATE_STMT;
        }
        return OTHER_STMT;
    }

    @Override
    protected void onAllReferencesReleased() {
        releaseCompiledSqlIfNotInCache();
@@ -361,7 +345,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
     */
    public void clearBindings() {
        synchronized (this) {
            bindArgs = null;
            mBindArgs = null;
            if (this.nStatement == 0) {
                return;
            }
@@ -380,7 +364,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
     */
    public void close() {
        synchronized (this) {
            bindArgs = null;
            mBindArgs = null;
            if (nHandle == 0 || !mDatabase.isOpen()) {
                return;
            }
@@ -389,19 +373,19 @@ public abstract class SQLiteProgram extends SQLiteClosable {
    }

    private synchronized void addToBindArgs(int index, Object value) {
        if (bindArgs == null) {
            bindArgs = new ArrayList<Pair<Integer, Object>>();
        if (mBindArgs == null) {
            mBindArgs = new ArrayList<Pair<Integer, Object>>();
        }
        bindArgs.add(new Pair<Integer, Object>(index, value));
        mBindArgs.add(new Pair<Integer, Object>(index, value));
    }

    /* package */ synchronized void compileAndbindAllArgs() {
        assert nStatement == 0;
        compileSql();
        if (bindArgs == null) {
        if (mBindArgs == null) {
            return;
        }
        for (Pair<Integer, Object> p : bindArgs) {
        for (Pair<Integer, Object> p : mBindArgs) {
            if (p.second == null) {
                native_bind_null(p.first);
            } else if (p.second instanceof Long) {
Loading