Loading api/current.xml +90 −0 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -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" core/java/android/database/DatabaseUtils.java +56 −0 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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; } } core/java/android/database/sqlite/DatabaseConnectionPool.java +3 −0 Original line number Diff line number Diff line Loading @@ -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(); } Loading core/java/android/database/sqlite/SQLiteDatabase.java +81 −65 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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. Loading Loading @@ -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 { Loading Loading @@ -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; } /** Loading Loading @@ -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()); Loading Loading @@ -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(); Loading @@ -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 { Loading Loading @@ -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); Loading Loading @@ -1625,7 +1673,6 @@ public class SQLiteDatabase extends SQLiteClosable { sql.append(values); sql.append(");"); lock(); SQLiteStatement statement = null; try { statement = compileStatement(sql.toString()); Loading @@ -1649,7 +1696,6 @@ public class SQLiteDatabase extends SQLiteClosable { if (statement != null) { statement.close(); } unlock(); } } Loading @@ -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 Loading @@ -1686,7 +1730,6 @@ public class SQLiteDatabase extends SQLiteClosable { if (statement != null) { statement.close(); } unlock(); } } Loading Loading @@ -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"); } Loading Loading @@ -1746,7 +1788,6 @@ public class SQLiteDatabase extends SQLiteClosable { } verifyDbIsOpen(); lock(); SQLiteStatement statement = null; try { statement = compileStatement(sql.toString()); Loading Loading @@ -1781,7 +1822,6 @@ public class SQLiteDatabase extends SQLiteClosable { if (statement != null) { statement.close(); } unlock(); } } Loading @@ -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> Loading @@ -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); Loading Loading @@ -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); Loading @@ -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; Loading @@ -1910,7 +1932,6 @@ public class SQLiteDatabase extends SQLiteClosable { if (statement != null) { statement.close(); } unlock(); } logTimeStat(sql, timeStart); } Loading Loading @@ -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(); Loading Loading @@ -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(); Loading @@ -2246,9 +2264,8 @@ public class SQLiteDatabase extends SQLiteClosable { /** * for testing only * @hide */ public ArrayList<Integer> getQueuedUpStmtList() { /* package */ ArrayList<Integer> getQueuedUpStmtList() { return mClosedStatementIds; } Loading Loading @@ -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) { Loading Loading @@ -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 Loading Loading @@ -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); } Loading core/java/android/database/sqlite/SQLiteProgram.java +14 −30 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.database.sqlite; import android.database.DatabaseUtils; import android.util.Log; import android.util.Pair; Loading @@ -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 */ Loading Loading @@ -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); Loading @@ -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; Loading @@ -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. Loading Loading @@ -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(); Loading Loading @@ -361,7 +345,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { */ public void clearBindings() { synchronized (this) { bindArgs = null; mBindArgs = null; if (this.nStatement == 0) { return; } Loading @@ -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; } Loading @@ -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 Loading
api/current.xml +90 −0 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -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"
core/java/android/database/DatabaseUtils.java +56 −0 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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; } }
core/java/android/database/sqlite/DatabaseConnectionPool.java +3 −0 Original line number Diff line number Diff line Loading @@ -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(); } Loading
core/java/android/database/sqlite/SQLiteDatabase.java +81 −65 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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. Loading Loading @@ -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 { Loading Loading @@ -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; } /** Loading Loading @@ -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()); Loading Loading @@ -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(); Loading @@ -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 { Loading Loading @@ -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); Loading Loading @@ -1625,7 +1673,6 @@ public class SQLiteDatabase extends SQLiteClosable { sql.append(values); sql.append(");"); lock(); SQLiteStatement statement = null; try { statement = compileStatement(sql.toString()); Loading @@ -1649,7 +1696,6 @@ public class SQLiteDatabase extends SQLiteClosable { if (statement != null) { statement.close(); } unlock(); } } Loading @@ -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 Loading @@ -1686,7 +1730,6 @@ public class SQLiteDatabase extends SQLiteClosable { if (statement != null) { statement.close(); } unlock(); } } Loading Loading @@ -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"); } Loading Loading @@ -1746,7 +1788,6 @@ public class SQLiteDatabase extends SQLiteClosable { } verifyDbIsOpen(); lock(); SQLiteStatement statement = null; try { statement = compileStatement(sql.toString()); Loading Loading @@ -1781,7 +1822,6 @@ public class SQLiteDatabase extends SQLiteClosable { if (statement != null) { statement.close(); } unlock(); } } Loading @@ -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> Loading @@ -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); Loading Loading @@ -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); Loading @@ -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; Loading @@ -1910,7 +1932,6 @@ public class SQLiteDatabase extends SQLiteClosable { if (statement != null) { statement.close(); } unlock(); } logTimeStat(sql, timeStart); } Loading Loading @@ -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(); Loading Loading @@ -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(); Loading @@ -2246,9 +2264,8 @@ public class SQLiteDatabase extends SQLiteClosable { /** * for testing only * @hide */ public ArrayList<Integer> getQueuedUpStmtList() { /* package */ ArrayList<Integer> getQueuedUpStmtList() { return mClosedStatementIds; } Loading Loading @@ -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) { Loading Loading @@ -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 Loading Loading @@ -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); } Loading
core/java/android/database/sqlite/SQLiteProgram.java +14 −30 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.database.sqlite; import android.database.DatabaseUtils; import android.util.Log; import android.util.Pair; Loading @@ -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 */ Loading Loading @@ -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); Loading @@ -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; Loading @@ -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. Loading Loading @@ -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(); Loading Loading @@ -361,7 +345,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { */ public void clearBindings() { synchronized (this) { bindArgs = null; mBindArgs = null; if (this.nStatement == 0) { return; } Loading @@ -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; } Loading @@ -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