Revert "caching code retooled to reduce locking + handle SMP"

This reverts commit 992f7d52fa.

Change-Id: Ia5b789d1b2195d6ce43baffe24296c857f9b30f6
This commit is contained in:
Vasu Nori
2010-09-14 11:35:49 -07:00
parent d3326231dc
commit b729dcc8a9
10 changed files with 299 additions and 637 deletions

View File

@@ -99,7 +99,7 @@ import java.util.Random;
poolObj = mPool.get(0);
} else {
for (int i = 0; i < mMaxPoolSize; i++) {
if (mPool.get(i).mDb.mCache.isSqlInStatementCache(sql)) {
if (mPool.get(i).mDb.isSqlInStatementCache(sql)) {
poolObj = mPool.get(i);
break;
}
@@ -125,8 +125,7 @@ import java.util.Random;
// there are free connections available. pick one
// preferably a connection caching the pre-compiled statement of the given SQL
for (int i = 0; i < poolSize; i++) {
if (mPool.get(i).isFree() &&
mPool.get(i).mDb.mCache.isSqlInStatementCache(sql)) {
if (mPool.get(i).isFree() && mPool.get(i).mDb.isSqlInStatementCache(sql)) {
poolObj = mPool.get(i);
break;
}

View File

@@ -1,214 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.database.sqlite;
import android.util.Log;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* For each instance of {@link SQLiteDatabase}, this class maintains a LRU cache to store
* the compiled query statement ids returned by sqlite database.
*<p>
*<ul>
* <li>key = SQL statement with "?" for bind args</li>
* <li>value = {@link SQLiteCompiledSql}</li>
*</ul>
* If an application opens the database and keeps it open during its entire life, then
* there will not be an overhead of compilation of SQL statements by sqlite.
*<p>
* Why is this cache NOT static? because sqlite attaches compiled-sql statements to the
* database connections.
*<p>
* This cache has an upper limit of mMaxSqlCacheSize (settable by calling the method
* (@link #setMaxSqlCacheSize(int)}).
*/
/* package */ class SQLiteCache {
private static final String TAG = "SQLiteCache";
/** The {@link SQLiteDatabase} instance this cache is attached to */
private final SQLiteDatabase mDatabase;
/** Default statement-cache size per database connection ( = instance of this class) */
private int mMaxSqlCacheSize = 25;
/** The LRU cache */
private final Map<String, SQLiteCompiledSql> mCompiledQueries =
new LinkedHashMap<String, SQLiteCompiledSql>(mMaxSqlCacheSize + 1, 0.75f, true) {
@Override
public boolean removeEldestEntry(Map.Entry<String, SQLiteCompiledSql> eldest) {
// eldest = least-recently used entry
// if it needs to be removed to accommodate a new entry,
// close {@link SQLiteCompiledSql} represented by this entry, if not in use
// and then let it be removed from the Map.
mDatabase.verifyLockOwner();
if (this.size() <= mMaxSqlCacheSize) {
// cache is not full. nothing needs to be removed
return false;
}
// cache is full. eldest will be removed.
eldest.getValue().releaseIfNotInUse();
// return true, so that this entry is removed automatically by the caller.
return true;
}
};
/** Maintains whether or not cacheFullWarning has been logged */
private boolean mCacheFullWarning;
/** The following 2 members maintain stats about cache hits and misses */
private int mNumCacheHits;
private int mNumCacheMisses;
/**
* Constructor used by {@link SQLiteDatabase}.
* @param db
*/
/* package */ SQLiteCache(SQLiteDatabase db) {
mDatabase = db;
}
/**
* Adds the given SQL and its compiled-statement to the cache, if the given SQL statement
* doesn't already exist in cache.
*
* @return true if added to cache. false otherwise.
*/
/* package */ synchronized boolean addToCompiledQueries(String sql,
SQLiteCompiledSql compiledStatement) {
if (mCompiledQueries.containsKey(sql)) {
// already exists.
return false;
}
/* add the given SQLiteCompiledSql compiledStatement to cache.
* no need to worry about the cache size - because {@link #mCompiledQueries}
* self-limits its size to {@link #mMaxSqlCacheSize}.
*/
mCompiledQueries.put(sql, compiledStatement);
// need to log a warning to say that the cache is full?
if (!isCacheFullWarningLogged() && mCompiledQueries.size() == mMaxSqlCacheSize) {
/*
* cache size of {@link #mMaxSqlCacheSize} is not enough for this app.
* log a warning.
* chances are it is NOT using ? for bindargs - or cachesize is too small.
*/
Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
mDatabase.getPath() +
". Use setMaxSqlCacheSize() in SQLiteDatabase to increase cachesize. ");
setCacheFullWarningLogged();
}
return true;
}
/**
* Returns the compiled-statement for the given SQL statement, if the entry exists in cache
* and is free to use. Returns null otherwise.
* <p>
* If a compiled-sql statement is returned for the caller, it is reserved for the caller.
* So, don't use this method unless the caller needs to acquire the object.
*/
/* package */ synchronized SQLiteCompiledSql getCompiledStatementForSql(String sql) {
SQLiteCompiledSql compiledStatement = mCompiledQueries.get(sql);
if (compiledStatement == null) {
mNumCacheMisses++;
return null;
}
mNumCacheHits++;
// reserve it for the caller, if it is not already in use
if (!compiledStatement.acquire()) {
// couldn't acquire it since it is already in use. bug in app?
if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
Log.w(TAG, "Possible bug: Either using the same SQL in 2 threads at " +
" the same time, or previous instance of this SQL statement was " +
"never close()d. " + compiledStatement.toString());
}
return null;
}
return compiledStatement;
}
/**
* If the given statement is in cache, it is released back to cache and it is made available for
* others to use.
* <p>
* return true if the statement is put back in cache, false otherwise (false = the statement
* is NOT in cache)
*/
/* package */ synchronized boolean releaseBackToCache(SQLiteCompiledSql stmt) {
if (!mCompiledQueries.containsValue(stmt)) {
return false;
}
// it is in cache. release it from the caller, make it available for others to use
stmt.free();
return true;
}
/**
* releases all compiled-sql statements in the cache.
*/
/* package */ synchronized void dealloc() {
for (SQLiteCompiledSql stmt : mCompiledQueries.values()) {
stmt.setState(SQLiteCompiledSql.NSTATE_CACHE_DEALLOC);
stmt.releaseFromDatabase();
}
mCompiledQueries.clear();
}
/**
* see documentation on {@link SQLiteDatabase#setMaxSqlCacheSize(int)}.
*/
/* package */ synchronized void setMaxSqlCacheSize(int cacheSize) {
if (cacheSize > SQLiteDatabase.MAX_SQL_CACHE_SIZE || cacheSize < 0) {
throw new IllegalStateException("expected value between 0 and " +
SQLiteDatabase.MAX_SQL_CACHE_SIZE);
} else if (cacheSize < mMaxSqlCacheSize) {
throw new IllegalStateException("cannot set cacheSize to a value less than the value " +
"set with previous setMaxSqlCacheSize() call.");
}
mMaxSqlCacheSize = cacheSize;
}
/* package */ synchronized boolean isSqlInStatementCache(String sql) {
return mCompiledQueries.containsKey(sql);
}
private synchronized boolean isCacheFullWarningLogged() {
return mCacheFullWarning;
}
private synchronized void setCacheFullWarningLogged() {
mCacheFullWarning = true;
}
/* package */ synchronized int getCacheHitNum() {
return mNumCacheHits;
}
/* package */ synchronized int getCacheMissNum() {
return mNumCacheMisses;
}
/* package */ synchronized int getCachesize() {
return mCompiledQueries.size();
}
// only for testing
/* package */ synchronized Set<String> getKeys() {
return mCompiledQueries.keySet();
}
}

View File

@@ -16,7 +16,6 @@
package android.database.sqlite;
import android.database.DatabaseUtils;
import android.util.Log;
/**
@@ -25,19 +24,19 @@ import android.util.Log;
* and it is released in one of the 2 following ways
* 1. when {@link SQLiteDatabase} object is closed.
* 2. if this is not cached in {@link SQLiteDatabase}, {@link android.database.Cursor#close()}
* releases this obj.
* releaases this obj.
*/
/* package */ class SQLiteCompiledSql {
private static final String TAG = "SQLiteCompiledSql";
/** The database this program is compiled against. */
/* package */ final SQLiteDatabase mDatabase;
/* package */ SQLiteDatabase mDatabase;
/**
* Native linkage, do not modify. This comes from the database.
*/
/* package */ final int nHandle;
/* package */ int nHandle = 0;
/**
* Native linkage, do not modify. When non-0 this holds a reference to a valid
@@ -47,53 +46,52 @@ import android.util.Log;
*/
/* package */ int nStatement = 0;
/** the following 3 members are for debugging purposes */
private final String mSqlStmt;
private final Throwable mStackTrace;
private int nState = 0;
/** values the above member can take */
private static final int NSTATE_CACHEABLE = 64;
private static final int NSTATE_IN_CACHE = 32;
private static final int NSTATE_INUSE = 16;
private static final int NSTATE_INUSE_RESETMASK = 0x0f;
/* package */ static final int NSTATE_CLOSE_NOOP = 1;
private static final int NSTATE_EVICTED_FROM_CACHE = 2;
/* package */ static final int NSTATE_CACHE_DEALLOC = 4;
private static final int NSTATE_IN_FINALIZER_Q = 8;
/** the following are for debugging purposes */
private String mSqlStmt = null;
private Throwable mStackTrace = null;
private SQLiteCompiledSql(SQLiteDatabase db, String sql) {
db.verifyDbIsOpen();
db.verifyLockOwner();
/** when in cache and is in use, this member is set */
private boolean mInUse = false;
/* package */ SQLiteCompiledSql(SQLiteDatabase db, String sql) {
if (!db.isOpen()) {
throw new IllegalStateException("database " + db.getPath() + " already closed");
}
mDatabase = db;
mSqlStmt = sql;
mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
nHandle = db.mNativeHandle;
native_compile(sql);
this.nHandle = db.mNativeHandle;
compile(sql, true);
}
/* package */ static SQLiteCompiledSql get(SQLiteDatabase db, String sql, int type) {
// only CRUD statements are cached.
if (type != DatabaseUtils.STATEMENT_SELECT && type != DatabaseUtils.STATEMENT_UPDATE) {
return new SQLiteCompiledSql(db, sql);
/**
* Compiles the given SQL into a SQLite byte code program using sqlite3_prepare_v2(). If
* this method has been called previously without a call to close and forCompilation is set
* to false the previous compilation will be used. Setting forceCompilation to true will
* always re-compile the program and should be done if you pass differing SQL strings to this
* method.
*
* <P>Note: this method acquires the database lock.</P>
*
* @param sql the SQL string to compile
* @param forceCompilation forces the SQL to be recompiled in the event that there is an
* existing compiled SQL program already around
*/
private void compile(String sql, boolean forceCompilation) {
mDatabase.verifyLockOwner();
// Only compile if we don't have a valid statement already or the caller has
// explicitly requested a recompile.
if (forceCompilation) {
// Note that the native_compile() takes care of destroying any previously
// existing programs before it compiles.
native_compile(sql);
}
// the given SQL statement is cacheable.
SQLiteCompiledSql stmt = db.mCache.getCompiledStatementForSql(sql);
if (stmt != null) {
return stmt;
}
// either the entry doesn't exist in cache or the one in cache is currently in use.
// try to add it to cache and let cache worry about what copy to keep
stmt = new SQLiteCompiledSql(db, sql);
stmt.nState |= NSTATE_CACHEABLE |
((db.mCache.addToCompiledQueries(sql, stmt)) ? NSTATE_IN_CACHE : 0);
return stmt;
}
/* package */ synchronized void releaseFromDatabase() {
/* package */ void releaseSqlStatement() {
// Note that native_finalize() checks to make sure that nStatement is
// non-null before destroying it.
if (nStatement != 0) {
nState |= NSTATE_IN_FINALIZER_Q;
mDatabase.finalizeStatementLater(nStatement);
nStatement = 0;
}
@@ -103,47 +101,20 @@ import android.util.Log;
* returns true if acquire() succeeds. false otherwise.
*/
/* package */ synchronized boolean acquire() {
if ((nState & NSTATE_INUSE) > 0 ) {
// this object is already in use
if (mInUse) {
// someone already has acquired it.
return false;
}
nState |= NSTATE_INUSE;
mInUse = true;
return true;
}
/* package */ synchronized void free() {
nState &= NSTATE_INUSE_RESETMASK;
/* package */ synchronized void release() {
mInUse = false;
}
/* package */ void release(int type) {
if (type != DatabaseUtils.STATEMENT_SELECT && type != DatabaseUtils.STATEMENT_UPDATE) {
// it is not cached. release its memory from the database.
releaseFromDatabase();
return;
}
// if in cache, reset its in-use flag
if (!mDatabase.mCache.releaseBackToCache(this)) {
// not in cache. release its memory from the database.
releaseFromDatabase();
}
}
/* package */ synchronized void releaseIfNotInUse() {
nState |= NSTATE_EVICTED_FROM_CACHE;
// if it is not in use, release its memory from the database
if ((nState & NSTATE_INUSE) == 0) {
releaseFromDatabase();
}
}
// only for testing purposes
/* package */ synchronized boolean isInUse() {
return (nState & NSTATE_INUSE) > 0;
}
/* package */ synchronized SQLiteCompiledSql setState(int val) {
nState = nState & val;
return this; // for chaining
return mInUse;
}
/**
@@ -154,18 +125,11 @@ import android.util.Log;
try {
if (nStatement == 0) return;
// finalizer should NEVER get called
// but if the database itself is not closed and is GC'ed, then
// all sub-objects attached to the database could end up getting GC'ed too.
// in that case, don't print any warning.
if ((nState & NSTATE_INUSE) == 0) {
// no need to print warning
} else {
int len = mSqlStmt.length();
Log.w(TAG, "Releasing SQL statement in finalizer. " +
"Could be due to close() not being called on the cursor or on the database. " +
toString(), mStackTrace);
}
releaseFromDatabase();
int len = mSqlStmt.length();
Log.w(TAG, "Releasing statement in a finalizer. Please ensure " +
"that you explicitly call close() on your cursor: " +
mSqlStmt.substring(0, (len > 100) ? 100 : len), mStackTrace);
releaseSqlStatement();
} finally {
super.finalize();
}
@@ -176,27 +140,6 @@ import android.util.Log;
StringBuilder buff = new StringBuilder();
buff.append(" nStatement=");
buff.append(nStatement);
if ((nState & NSTATE_CACHEABLE) > 0) {
buff.append(",cacheable");
}
if ((nState & NSTATE_IN_CACHE) > 0) {
buff.append(",cached");
}
if ((nState & NSTATE_INUSE) > 0) {
buff.append(",in_use");
}
if ((nState & NSTATE_CLOSE_NOOP) > 0) {
buff.append(",no_op_close");
}
if ((nState & NSTATE_EVICTED_FROM_CACHE) > 0) {
buff.append(",evicted_from_cache");
}
if ((nState & NSTATE_CACHE_DEALLOC) > 0) {
buff.append(",dealloc_cache");
}
if ((nState & NSTATE_IN_FINALIZER_Q) > 0) {
buff.append(",in dbFInalizerQ");
}
buff.append(", db=");
buff.append(mDatabase.getPath());
buff.append(", db_connectionNum=");

View File

@@ -41,6 +41,7 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
@@ -259,11 +260,60 @@ public class SQLiteDatabase extends SQLiteClosable {
private final WeakHashMap<SQLiteClosable, Object> mPrograms;
/**
* Absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}.
* Size of each prepared-statement is between 1K - 6K, depending on the complexity of the
* for each instance of this class, a LRU cache is maintained to store
* the compiled query statement ids returned by sqlite database.
* key = SQL statement with "?" for bind args
* value = {@link SQLiteCompiledSql}
* If an application opens the database and keeps it open during its entire life, then
* there will not be an overhead of compilation of SQL statements by sqlite.
*
* why is this cache NOT static? because sqlite attaches compiledsql statements to the
* struct created when {@link SQLiteDatabase#openDatabase(String, CursorFactory, int)} is
* invoked.
*
* this cache has an upper limit of mMaxSqlCacheSize (settable by calling the method
* (@link #setMaxSqlCacheSize(int)}).
*/
// default statement-cache size per database connection ( = instance of this class)
private int mMaxSqlCacheSize = 25;
/* package */ final Map<String, SQLiteCompiledSql> mCompiledQueries =
new LinkedHashMap<String, SQLiteCompiledSql>(mMaxSqlCacheSize + 1, 0.75f, true) {
@Override
public boolean removeEldestEntry(Map.Entry<String, SQLiteCompiledSql> eldest) {
// eldest = least-recently used entry
// if it needs to be removed to accommodate a new entry,
// close {@link SQLiteCompiledSql} represented by this entry, if not in use
// and then let it be removed from the Map.
// when this is called, the caller must be trying to add a just-compiled stmt
// to cache; i.e., caller should already have acquired database lock AND
// the lock on mCompiledQueries. do as assert of these two 2 facts.
verifyLockOwner();
if (this.size() <= mMaxSqlCacheSize) {
// cache is not full. nothing needs to be removed
return false;
}
// cache is full. eldest will be removed.
SQLiteCompiledSql entry = eldest.getValue();
if (!entry.isInUse()) {
// this {@link SQLiteCompiledSql} is not in use. release it.
entry.releaseSqlStatement();
}
// return true, so that this entry is removed automatically by the caller.
return true;
}
};
/**
* absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}
* size of each prepared-statement is between 1K - 6K, depending on the complexity of the
* SQL statement & schema.
*/
public static final int MAX_SQL_CACHE_SIZE = 100;
private int mCacheFullWarnings;
private static final int MAX_WARNINGS_ON_CACHESIZE_CONDITION = 1;
/** maintain stats about number of cache hits and misses */
private int mNumCacheHits;
private int mNumCacheMisses;
/** Used to find out where this object was created in case it never got closed. */
private final Throwable mStackTrace;
@@ -272,9 +322,6 @@ public class SQLiteDatabase extends SQLiteClosable {
private static final String LOG_SLOW_QUERIES_PROPERTY = "db.log.slow_query_threshold";
private final int mSlowQueryThreshold;
/** the LRU cache */
/* package */ final SQLiteCache mCache = new SQLiteCache(this);
/** stores the list of statement ids that need to be finalized by sqlite */
private final ArrayList<Integer> mClosedStatementIds = new ArrayList<Integer>();
@@ -1040,7 +1087,7 @@ public class SQLiteDatabase extends SQLiteClosable {
* to be closed. sqlite doesn't let a database close if there are
* any unfinalized statements - such as the compiled-sql objects in mCompiledQueries.
*/
mCache.dealloc();
deallocCachedSqlStatements();
Iterator<Map.Entry<SQLiteClosable, Object>> iter = mPrograms.entrySet().iterator();
while (iter.hasNext()) {
@@ -2043,20 +2090,113 @@ public class SQLiteDatabase extends SQLiteClosable {
}
}
/*
* ============================================================================
*
* The following methods deal with compiled-sql cache
* ============================================================================
*/
/**
* Adds the given SQL and its compiled-statement-id-returned-by-sqlite to the
* cache of compiledQueries attached to 'this'.
* <p>
* If there is already a {@link SQLiteCompiledSql} in compiledQueries for the given SQL,
* the new {@link SQLiteCompiledSql} object is NOT inserted into the cache (i.e.,the current
* mapping is NOT replaced with the new mapping).
*/
/* package */ void addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) {
synchronized(mCompiledQueries) {
// don't insert the new mapping if a mapping already exists
if (mCompiledQueries.containsKey(sql)) {
return;
}
if (mCompiledQueries.size() == mMaxSqlCacheSize) {
/*
* cache size of {@link #mMaxSqlCacheSize} is not enough for this app.
* log a warning.
* chances are it is NOT using ? for bindargs - or cachesize is too small.
*/
if (++mCacheFullWarnings == MAX_WARNINGS_ON_CACHESIZE_CONDITION) {
Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
getPath() + ". Use setMaxSqlCacheSize() to increase cachesize. ");
}
}
/* add the given SQLiteCompiledSql compiledStatement to cache.
* no need to worry about the cache size - because {@link #mCompiledQueries}
* self-limits its size to {@link #mMaxSqlCacheSize}.
*/
mCompiledQueries.put(sql, compiledStatement);
if (SQLiteDebug.DEBUG_SQL_CACHE) {
Log.v(TAG, "|adding_sql_to_cache|" + getPath() + "|" +
mCompiledQueries.size() + "|" + sql);
}
}
}
/** package-level access for testing purposes */
/* package */ void deallocCachedSqlStatements() {
synchronized (mCompiledQueries) {
for (SQLiteCompiledSql compiledSql : mCompiledQueries.values()) {
compiledSql.releaseSqlStatement();
}
mCompiledQueries.clear();
}
}
/**
* From the compiledQueries cache, returns the compiled-statement-id for the given SQL.
* Returns null, if not found in the cache.
*/
/* package */ SQLiteCompiledSql getCompiledStatementForSql(String sql) {
SQLiteCompiledSql compiledStatement = null;
boolean cacheHit;
synchronized(mCompiledQueries) {
cacheHit = (compiledStatement = mCompiledQueries.get(sql)) != null;
}
if (cacheHit) {
mNumCacheHits++;
} else {
mNumCacheMisses++;
}
if (SQLiteDebug.DEBUG_SQL_CACHE) {
Log.v(TAG, "|cache_stats|" +
getPath() + "|" + mCompiledQueries.size() +
"|" + mNumCacheHits + "|" + mNumCacheMisses +
"|" + cacheHit + "|" + sql);
}
return compiledStatement;
}
/**
* Sets the maximum size of the prepared-statement cache for this database.
* (size of the cache = number of compiled-sql-statements stored in the cache).
*<p>
* Maximum cache size can ONLY be increased from its current size (default = 25).
* Maximum cache size can ONLY be increased from its current size (default = 10).
* If this method is called with smaller size than the current maximum value,
* then IllegalStateException is thrown.
*<p>
* This method is thread-safe.
*
* @param cacheSize the size of the cache. can be (0 to {@link #MAX_SQL_CACHE_SIZE})
* @throws IllegalStateException if input cacheSize > {@link #MAX_SQL_CACHE_SIZE} or
* the value set with previous setMaxSqlCacheSize() call.
*/
public void setMaxSqlCacheSize(int cacheSize) {
mCache.setMaxSqlCacheSize(cacheSize);
public synchronized void setMaxSqlCacheSize(int cacheSize) {
if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
throw new IllegalStateException("expected value between 0 and " + MAX_SQL_CACHE_SIZE);
} else if (cacheSize < mMaxSqlCacheSize) {
throw new IllegalStateException("cannot set cacheSize to a value less than the value " +
"set with previous setMaxSqlCacheSize() call.");
}
mMaxSqlCacheSize = cacheSize;
}
/* package */ boolean isSqlInStatementCache(String sql) {
synchronized (mCompiledQueries) {
return mCompiledQueries.containsKey(sql);
}
}
/* package */ void finalizeStatementLater(int id) {
@@ -2212,19 +2352,17 @@ public class SQLiteDatabase extends SQLiteClosable {
*
* @param size the value the connection handle pool size should be set to.
*/
public void setConnectionPoolSize(int size) {
synchronized(this) {
if (mConnectionPool == null) {
throw new IllegalStateException("connection pool not enabled");
}
int i = mConnectionPool.getMaxPoolSize();
if (size < i) {
throw new IllegalArgumentException(
"cannot set max pool size to a value less than the current max value(=" +
i + ")");
}
mConnectionPool.setMaxPoolSize(size);
public synchronized void setConnectionPoolSize(int size) {
if (mConnectionPool == null) {
throw new IllegalStateException("connection pool not enabled");
}
int i = mConnectionPool.getMaxPoolSize();
if (size < i) {
throw new IllegalArgumentException(
"cannot set max pool size to a value less than the current max value(=" +
i + ")");
}
mConnectionPool.setMaxPoolSize(size);
}
/* package */ SQLiteDatabase createPoolConnection(short connectionNum) {
@@ -2340,16 +2478,16 @@ public class SQLiteDatabase extends SQLiteClosable {
}
if (pageCount > 0) {
dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
lookasideUsed, db.mCache.getCacheHitNum(),
db.mCache.getCacheMissNum(), db.mCache.getCachesize()));
lookasideUsed, db.mNumCacheHits, db.mNumCacheMisses,
db.mCompiledQueries.size()));
}
}
// if there are pooled connections, return the cache stats for them also.
if (db.mConnectionPool != null) {
for (SQLiteDatabase pDb : db.mConnectionPool.getConnectionList()) {
dbStatsList.add(new DbStats("(pooled # " + pDb.mConnectionNum + ") "
+ lastnode, 0, 0, 0, pDb.mCache.getCacheHitNum(),
pDb.mCache.getCacheMissNum(), pDb.mCache.getCachesize()));
+ lastnode, 0, 0, 0, pDb.mNumCacheHits, pDb.mNumCacheMisses,
pDb.mCompiledQueries.size()));
}
}
} catch (SQLiteException e) {

View File

@@ -110,6 +110,42 @@ public abstract class SQLiteProgram extends SQLiteClosable {
}
}
private void compileSql() {
// only cache CRUD statements
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.
return;
}
mCompiledSql = mDatabase.getCompiledStatementForSql(mSql);
if (mCompiledSql == null) {
// create a new compiled-sql obj
mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
// add it to the cache of compiled-sqls
// but before adding it and thus making it available for anyone else to use it,
// make sure it is acquired by me.
mCompiledSql.acquire();
mDatabase.addToCompiledQueries(mSql, mCompiledSql);
} else {
// it is already in compiled-sql cache.
// try to acquire the object.
if (!mCompiledSql.acquire()) {
int last = mCompiledSql.nStatement;
// the SQLiteCompiledSql in cache is in use by some other SQLiteProgram object.
// we can't have two different SQLiteProgam objects can't share the same
// CompiledSql object. create a new one.
// finalize it when I am done with it in "this" object.
mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
// since it is not in the cache, no need to acquire() it.
}
}
nStatement = mCompiledSql.nStatement;
}
@Override
protected void onAllReferencesReleased() {
release();
@@ -127,7 +163,16 @@ public abstract class SQLiteProgram extends SQLiteClosable {
if (mCompiledSql == null) {
return;
}
mCompiledSql.release(mStatementType);
synchronized(mDatabase.mCompiledQueries) {
if (!mDatabase.mCompiledQueries.containsValue(mCompiledSql)) {
// it is NOT in compiled-sql cache. i.e., responsibility of
// releasing this statement is on me.
mCompiledSql.releaseSqlStatement();
} else {
// it is in compiled-sql cache. reset its CompiledSql#mInUse flag
mCompiledSql.release();
}
}
mCompiledSql = null;
nStatement = 0;
}
@@ -288,9 +333,6 @@ public abstract class SQLiteProgram extends SQLiteClosable {
synchronized (this) {
mBindArgs = null;
if (nHandle == 0 || !mDatabase.isOpen()) {
if (mCompiledSql != null) {
mCompiledSql.setState(SQLiteCompiledSql.NSTATE_CLOSE_NOOP);
}
return;
}
releaseReference();
@@ -307,8 +349,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
/* package */ synchronized void compileAndbindAllArgs() {
if (nStatement == 0) {
// SQL statement is not compiled yet. compile it now.
mCompiledSql = SQLiteCompiledSql.get(mDatabase, mSql, mStatementType);
nStatement = mCompiledSql.nStatement;
compileSql();
}
if (mBindArgs == null) {
return;