Loading api/current.xml +27 −3 Original line number Diff line number Diff line Loading @@ -50825,6 +50825,17 @@ <exception name="SQLException" type="android.database.SQLException"> </exception> </method> <method name="resetCompiledSqlCache" return="void" abstract="false" native="false" synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > </method> <method name="setLocale" return="void" abstract="false" Loading @@ -50851,6 +50862,19 @@ <parameter name="lockingEnabled" type="boolean"> </parameter> </method> <method name="setMaxSqlCacheSize" return="void" abstract="false" native="false" synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > <parameter name="cacheSize" type="int"> </parameter> </method> <method name="setMaximumSize" return="long" abstract="false" Loading Loading @@ -51411,7 +51435,7 @@ synchronized="false" static="false" final="false" deprecated="not deprecated" deprecated="deprecated" visibility="protected" > <parameter name="sql" type="java.lang.String"> Loading Loading @@ -51510,7 +51534,7 @@ synchronized="false" static="false" final="true" deprecated="not deprecated" deprecated="deprecated" visibility="protected" > <parameter name="sql" type="java.lang.String"> Loading @@ -51523,7 +51547,7 @@ synchronized="false" static="false" final="true" deprecated="not deprecated" deprecated="deprecated" visibility="protected" > </method> core/java/android/database/sqlite/SQLiteCompiledSql.java 0 → 100644 +111 −0 Original line number Diff line number Diff line /* * Copyright (C) 2009 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; /** * This class encapsulates compilation of sql statement and release of the compiled statement obj. * Once a sql statement is compiled, it is cached in {@link SQLiteDatabase} * and it is released in one of the 2 following ways * 1. when {@link SQLiteDatabase} object is closed. * 2. dalvikVM wants to reclaim some memory and releases it from the cache in * {@link SQLiteDatabase}. */ /* package */ class SQLiteCompiledSql { /** The database this program is compiled against. */ /* package */ SQLiteDatabase mDatabase; /** * Native linkage, do not modify. This comes from the database. */ /* package */ int nHandle = 0; /** * Native linkage, do not modify. When non-0 this holds a reference to a valid * sqlite3_statement object. It is only updated by the native code, but may be * checked in this class when the database lock is held to determine if there * is a valid native-side program or not. */ /* package */ int nStatement = 0; /* package */ SQLiteCompiledSql(SQLiteDatabase db, String sql) { mDatabase = db; this.nHandle = db.mNativeHandle; compile(sql, true); } /** * 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) { // Only compile if we don't have a valid statement already or the caller has // explicitly requested a recompile. if (forceCompilation) { mDatabase.lock(); try { // Note that the native_compile() takes care of destroying any previously // existing programs before it compiles. native_compile(sql); } finally { mDatabase.unlock(); } } } /* package */ void releaseSqlStatement() { // Note that native_finalize() checks to make sure that nStatement is // non-null before destroying it. if (nStatement != 0) { try { mDatabase.lock(); native_finalize(); nStatement = 0; } finally { mDatabase.unlock(); } } } /** * Make sure that the native resource is cleaned up. */ @Override protected void finalize() { releaseSqlStatement(); } /** * Compiles SQL into a SQLite program. * * <P>The database lock must be held when calling this method. * @param sql The SQL to compile. */ private final native void native_compile(String sql); private final native void native_finalize(); } core/java/android/database/sqlite/SQLiteDatabase.java +157 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.database.sqlite; import com.google.android.collect.Maps; import android.content.ContentValues; import android.database.Cursor; import android.database.DatabaseUtils; Loading @@ -29,6 +31,7 @@ import android.util.EventLog; import android.util.Log; import java.io.File; import java.text.SimpleDateFormat; import java.util.HashMap; import java.util.Iterator; import java.util.Locale; Loading Loading @@ -217,6 +220,34 @@ public class SQLiteDatabase extends SQLiteClosable { private WeakHashMap<SQLiteClosable, Object> mPrograms; /** * for each instance of this class, a 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 setMaxCacheSize(int)}). its default is 0 - i.e., no caching by default because * most of the apps don't use "?" syntax in their sql, caching is not useful for them. */ private Map<String, SQLiteCompiledSql> mCompiledQueries = Maps.newHashMap(); private int mMaxSqlCacheSize = 0; // no caching by default private static final int MAX_SQL_CACHE_SIZE = 1000; /** maintain stats about number of cache hits and misses */ private int mNumCacheHits; private int mNumCacheMisses; /** the following 2 members maintain the time when a database is opened and closed */ private String mTimeOpened = null; private String mTimeClosed = null; private final RuntimeException mLeakedException; // package visible, since callers will access directly to minimize overhead in the case Loading Loading @@ -251,6 +282,9 @@ public class SQLiteDatabase extends SQLiteClosable { @Override protected void onAllReferencesReleased() { if (isOpen()) { if (SQLiteDebug.DEBUG_SQL_CACHE) { mTimeClosed = getTime(); } dbclose(); } } Loading Loading @@ -798,6 +832,13 @@ public class SQLiteDatabase extends SQLiteClosable { program.onAllReferencesReleasedFromContainer(); } } // finalize all compiled sql statement objects in compiledQueries cache synchronized (mCompiledQueries) { for (SQLiteCompiledSql compiledStatement : mCompiledQueries.values()) { compiledStatement.releaseSqlStatement(); } } } /** Loading Loading @@ -1695,16 +1736,26 @@ public class SQLiteDatabase extends SQLiteClosable { " SQLiteDatabase created and never closed"); mFactory = factory; dbopen(mPath, mFlags); if (SQLiteDebug.DEBUG_SQL_CACHE) { mTimeOpened = getTime(); } mPrograms = new WeakHashMap<SQLiteClosable,Object>(); try { setLocale(Locale.getDefault()); } catch (RuntimeException e) { Log.e(TAG, "Failed to setLocale() when constructing, closing the database", e); dbclose(); if (SQLiteDebug.DEBUG_SQL_CACHE) { mTimeClosed = getTime(); } throw e; } } private String getTime() { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS ").format(System.currentTimeMillis()); } /** * return whether the DB is opened as read only. * @return true if DB is opened as read only Loading Loading @@ -1733,6 +1784,112 @@ public class SQLiteDatabase extends SQLiteClosable { return mPath; } /** * set the max size of the compiled sql cache for this database after purging the cache. * (size of the cache = number of compiled-sql-statements stored in the cache) * * synchronized because we don't want t threads to change cache size at the same time. * @param cacheSize the size of the cache. can be (0 to MAX_SQL_CACHE_SIZE) */ public void setMaxSqlCacheSize(int cacheSize) { synchronized(mCompiledQueries) { resetCompiledSqlCache(); mMaxSqlCacheSize = (cacheSize > MAX_SQL_CACHE_SIZE) ? MAX_SQL_CACHE_SIZE : (cacheSize < 0) ? 0 : cacheSize; } } /** * remove everything from the compiled sql cache */ public void resetCompiledSqlCache() { synchronized(mCompiledQueries) { mCompiledQueries.clear(); } } /** * adds the given sql and its compiled-statement-id-returned-by-sqlite to the * cache of compiledQueries attached to 'this'. * * 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). * * @return true if the given obj is added to cache. false otherwise. */ /* package */ boolean addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) { if (mMaxSqlCacheSize == 0) { // for this database, there is no cache of compiled sql. if (SQLiteDebug.DEBUG_SQL_CACHE) { Log.v(TAG, "|NOT adding_sql_to_cache|" + getPath() + "|" + sql); } return false; } SQLiteCompiledSql compiledSql = null; synchronized(mCompiledQueries) { // don't insert the new mapping if a mapping already exists compiledSql = mCompiledQueries.get(sql); if (compiledSql != null) { return false; } // add this <sql, compiledStatement> to the cache if (mCompiledQueries.size() == mMaxSqlCacheSize) { /* reached max cachesize. before adding new entry, remove an entry from the * cache. we don't want to wipe out the entire cache because of this: * GCing {@link SQLiteCompiledSql} requires call to sqlite3_finalize * JNI method. If entire cache is wiped out, it could be cause a big GC activity * just because a (rogue) process is using the cache incorrectly. */ Set<String> keySet = mCompiledQueries.keySet(); for (String s : keySet) { mCompiledQueries.remove(s); break; } } compiledSql = new SQLiteCompiledSql(this, sql); mCompiledQueries.put(sql, compiledSql); } if (SQLiteDebug.DEBUG_SQL_CACHE) { Log.v(TAG, "|adding_sql_to_cache|" + getPath() + "|" + mCompiledQueries.size() + "|" + sql); } return true; } /** * 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) { if (mMaxSqlCacheSize == 0) { // for this database, there is no cache of compiled sql. if (SQLiteDebug.DEBUG_SQL_CACHE) { Log.v(TAG, "|cache NOT found|" + getPath()); } return null; } 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 + "|" + mTimeOpened + "|" + mTimeClosed + "|" + sql); } return compiledStatement; } /* package */ void logTimeStat(boolean read, long begin, long end) { EventLog.writeEvent(EVENT_DB_OPERATION, mPath, read ? 0 : 1, end - begin); } Loading core/java/android/database/sqlite/SQLiteDebug.java +6 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,12 @@ public final class SQLiteDebug { public static final boolean DEBUG_SQL_STATEMENTS = Log.isLoggable("SQLiteStatements", Log.VERBOSE); /** * Controls the printing of compiled-sql-statement cache stats. */ public static final boolean DEBUG_SQL_CACHE = Log.isLoggable("SQLiteCompiledSql", Log.VERBOSE); /** * Controls the stack trace reporting of active cursors being * finalized. Loading core/java/android/database/sqlite/SQLiteProgram.java +55 −70 Original line number Diff line number Diff line Loading @@ -27,6 +27,9 @@ public abstract class SQLiteProgram extends SQLiteClosable { /** The database this program is compiled against. */ protected SQLiteDatabase mDatabase; /** The SQL used to create this query */ /* package */ final String mSql; /** * Native linkage, do not modify. This comes from the database and should not be modified * in here or in the native code. Loading @@ -34,45 +37,60 @@ public abstract class SQLiteProgram extends SQLiteClosable { protected int nHandle = 0; /** * Native linkage, do not modify. When non-0 this holds a reference to a valid * sqlite3_statement object. It is only updated by the native code, but may be * checked in this class when the database lock is held to determine if there * is a valid native-side program or not. * the compiledSql object for the given sql statement. */ protected int nStatement = 0; private SQLiteCompiledSql compiledSql; private boolean myCompiledSqlIsInCache; /** * Used to find out where a cursor was allocated in case it never got * released. * compiledSql statement id is populated with the corresponding object from the above * member compiledSql. * this member is used by the native_bind_* methods */ private StackTraceElement[] mStackTraceElements; protected int nStatement = 0; /* package */ SQLiteProgram(SQLiteDatabase db, String sql) { if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { mStackTraceElements = new Exception().getStackTrace(); Log.d(TAG, "processing sql: " + sql); } mDatabase = db; mSql = sql; db.acquireReference(); db.addSQLiteClosable(this); this.nHandle = db.mNativeHandle; compile(sql, false); compiledSql = db.getCompiledStatementForSql(sql); if (compiledSql == null) { // create a new compiled-sql obj compiledSql = new SQLiteCompiledSql(db, sql); // add it to the cache of compiled-sqls myCompiledSqlIsInCache = db.addToCompiledQueries(sql, compiledSql); } else { myCompiledSqlIsInCache = true; } nStatement = compiledSql.nStatement; } @Override protected void onAllReferencesReleased() { // Note that native_finalize() checks to make sure that nStatement is // non-null before destroying it. native_finalize(); // release the compiled sql statement used by me if it is NOT in cache if (!myCompiledSqlIsInCache) { compiledSql.releaseSqlStatement(); compiledSql = null; // so that GC doesn't call finalize() on it } mDatabase.releaseReference(); mDatabase.removeSQLiteClosable(this); } @Override protected void onAllReferencesReleasedFromContainer() { // Note that native_finalize() checks to make sure that nStatement is // non-null before destroying it. native_finalize(); // release the compiled sql statement used by me if it is NOT in cache if (!myCompiledSqlIsInCache) { compiledSql.releaseSqlStatement(); compiledSql = null; // so that GC doesn't call finalize() on it } mDatabase.releaseReference(); } Loading @@ -82,37 +100,23 @@ public abstract class SQLiteProgram extends SQLiteClosable { * @return a unique identifier for this program */ public final int getUniqueId() { return nStatement; return compiledSql.nStatement; } /* package */ String getSqlString() { return mSql; } /** * 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> * @deprecated use this.compiledStatement.compile instead * * @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 */ @Deprecated protected void compile(String sql, boolean forceCompilation) { // Only compile if we don't have a valid statement already or the caller has // explicitly requested a recompile. if (nStatement == 0 || forceCompilation) { mDatabase.lock(); try { // Note that the native_compile() takes care of destroying any previously // existing programs before it compiles. acquireReference(); native_compile(sql); } finally { releaseReference(); mDatabase.unlock(); } } // TODO is there a need for this? } /** Loading Loading @@ -224,34 +228,15 @@ public abstract class SQLiteProgram extends SQLiteClosable { } } /** * Make sure that the native resource is cleaned up. */ @Override protected void finalize() { if (nStatement != 0) { if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { String message = "Finalizing " + this + " that has not been closed"; Log.d(TAG, message + "\nThis cursor was created in:"); for (StackTraceElement ste : mStackTraceElements) { Log.d(TAG, " " + ste); } } // when in finalize() it is already removed from weakhashmap // so it is safe to not removed itself from db onAllReferencesReleasedFromContainer(); } } /** * Compiles SQL into a SQLite program. * * <P>The database lock must be held when calling this method. * @param sql The SQL to compile. */ @Deprecated protected final native void native_compile(String sql); @Deprecated protected final native void native_finalize(); protected final native void native_bind_null(int index); Loading Loading
api/current.xml +27 −3 Original line number Diff line number Diff line Loading @@ -50825,6 +50825,17 @@ <exception name="SQLException" type="android.database.SQLException"> </exception> </method> <method name="resetCompiledSqlCache" return="void" abstract="false" native="false" synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > </method> <method name="setLocale" return="void" abstract="false" Loading @@ -50851,6 +50862,19 @@ <parameter name="lockingEnabled" type="boolean"> </parameter> </method> <method name="setMaxSqlCacheSize" return="void" abstract="false" native="false" synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > <parameter name="cacheSize" type="int"> </parameter> </method> <method name="setMaximumSize" return="long" abstract="false" Loading Loading @@ -51411,7 +51435,7 @@ synchronized="false" static="false" final="false" deprecated="not deprecated" deprecated="deprecated" visibility="protected" > <parameter name="sql" type="java.lang.String"> Loading Loading @@ -51510,7 +51534,7 @@ synchronized="false" static="false" final="true" deprecated="not deprecated" deprecated="deprecated" visibility="protected" > <parameter name="sql" type="java.lang.String"> Loading @@ -51523,7 +51547,7 @@ synchronized="false" static="false" final="true" deprecated="not deprecated" deprecated="deprecated" visibility="protected" > </method>
core/java/android/database/sqlite/SQLiteCompiledSql.java 0 → 100644 +111 −0 Original line number Diff line number Diff line /* * Copyright (C) 2009 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; /** * This class encapsulates compilation of sql statement and release of the compiled statement obj. * Once a sql statement is compiled, it is cached in {@link SQLiteDatabase} * and it is released in one of the 2 following ways * 1. when {@link SQLiteDatabase} object is closed. * 2. dalvikVM wants to reclaim some memory and releases it from the cache in * {@link SQLiteDatabase}. */ /* package */ class SQLiteCompiledSql { /** The database this program is compiled against. */ /* package */ SQLiteDatabase mDatabase; /** * Native linkage, do not modify. This comes from the database. */ /* package */ int nHandle = 0; /** * Native linkage, do not modify. When non-0 this holds a reference to a valid * sqlite3_statement object. It is only updated by the native code, but may be * checked in this class when the database lock is held to determine if there * is a valid native-side program or not. */ /* package */ int nStatement = 0; /* package */ SQLiteCompiledSql(SQLiteDatabase db, String sql) { mDatabase = db; this.nHandle = db.mNativeHandle; compile(sql, true); } /** * 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) { // Only compile if we don't have a valid statement already or the caller has // explicitly requested a recompile. if (forceCompilation) { mDatabase.lock(); try { // Note that the native_compile() takes care of destroying any previously // existing programs before it compiles. native_compile(sql); } finally { mDatabase.unlock(); } } } /* package */ void releaseSqlStatement() { // Note that native_finalize() checks to make sure that nStatement is // non-null before destroying it. if (nStatement != 0) { try { mDatabase.lock(); native_finalize(); nStatement = 0; } finally { mDatabase.unlock(); } } } /** * Make sure that the native resource is cleaned up. */ @Override protected void finalize() { releaseSqlStatement(); } /** * Compiles SQL into a SQLite program. * * <P>The database lock must be held when calling this method. * @param sql The SQL to compile. */ private final native void native_compile(String sql); private final native void native_finalize(); }
core/java/android/database/sqlite/SQLiteDatabase.java +157 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.database.sqlite; import com.google.android.collect.Maps; import android.content.ContentValues; import android.database.Cursor; import android.database.DatabaseUtils; Loading @@ -29,6 +31,7 @@ import android.util.EventLog; import android.util.Log; import java.io.File; import java.text.SimpleDateFormat; import java.util.HashMap; import java.util.Iterator; import java.util.Locale; Loading Loading @@ -217,6 +220,34 @@ public class SQLiteDatabase extends SQLiteClosable { private WeakHashMap<SQLiteClosable, Object> mPrograms; /** * for each instance of this class, a 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 setMaxCacheSize(int)}). its default is 0 - i.e., no caching by default because * most of the apps don't use "?" syntax in their sql, caching is not useful for them. */ private Map<String, SQLiteCompiledSql> mCompiledQueries = Maps.newHashMap(); private int mMaxSqlCacheSize = 0; // no caching by default private static final int MAX_SQL_CACHE_SIZE = 1000; /** maintain stats about number of cache hits and misses */ private int mNumCacheHits; private int mNumCacheMisses; /** the following 2 members maintain the time when a database is opened and closed */ private String mTimeOpened = null; private String mTimeClosed = null; private final RuntimeException mLeakedException; // package visible, since callers will access directly to minimize overhead in the case Loading Loading @@ -251,6 +282,9 @@ public class SQLiteDatabase extends SQLiteClosable { @Override protected void onAllReferencesReleased() { if (isOpen()) { if (SQLiteDebug.DEBUG_SQL_CACHE) { mTimeClosed = getTime(); } dbclose(); } } Loading Loading @@ -798,6 +832,13 @@ public class SQLiteDatabase extends SQLiteClosable { program.onAllReferencesReleasedFromContainer(); } } // finalize all compiled sql statement objects in compiledQueries cache synchronized (mCompiledQueries) { for (SQLiteCompiledSql compiledStatement : mCompiledQueries.values()) { compiledStatement.releaseSqlStatement(); } } } /** Loading Loading @@ -1695,16 +1736,26 @@ public class SQLiteDatabase extends SQLiteClosable { " SQLiteDatabase created and never closed"); mFactory = factory; dbopen(mPath, mFlags); if (SQLiteDebug.DEBUG_SQL_CACHE) { mTimeOpened = getTime(); } mPrograms = new WeakHashMap<SQLiteClosable,Object>(); try { setLocale(Locale.getDefault()); } catch (RuntimeException e) { Log.e(TAG, "Failed to setLocale() when constructing, closing the database", e); dbclose(); if (SQLiteDebug.DEBUG_SQL_CACHE) { mTimeClosed = getTime(); } throw e; } } private String getTime() { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS ").format(System.currentTimeMillis()); } /** * return whether the DB is opened as read only. * @return true if DB is opened as read only Loading Loading @@ -1733,6 +1784,112 @@ public class SQLiteDatabase extends SQLiteClosable { return mPath; } /** * set the max size of the compiled sql cache for this database after purging the cache. * (size of the cache = number of compiled-sql-statements stored in the cache) * * synchronized because we don't want t threads to change cache size at the same time. * @param cacheSize the size of the cache. can be (0 to MAX_SQL_CACHE_SIZE) */ public void setMaxSqlCacheSize(int cacheSize) { synchronized(mCompiledQueries) { resetCompiledSqlCache(); mMaxSqlCacheSize = (cacheSize > MAX_SQL_CACHE_SIZE) ? MAX_SQL_CACHE_SIZE : (cacheSize < 0) ? 0 : cacheSize; } } /** * remove everything from the compiled sql cache */ public void resetCompiledSqlCache() { synchronized(mCompiledQueries) { mCompiledQueries.clear(); } } /** * adds the given sql and its compiled-statement-id-returned-by-sqlite to the * cache of compiledQueries attached to 'this'. * * 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). * * @return true if the given obj is added to cache. false otherwise. */ /* package */ boolean addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) { if (mMaxSqlCacheSize == 0) { // for this database, there is no cache of compiled sql. if (SQLiteDebug.DEBUG_SQL_CACHE) { Log.v(TAG, "|NOT adding_sql_to_cache|" + getPath() + "|" + sql); } return false; } SQLiteCompiledSql compiledSql = null; synchronized(mCompiledQueries) { // don't insert the new mapping if a mapping already exists compiledSql = mCompiledQueries.get(sql); if (compiledSql != null) { return false; } // add this <sql, compiledStatement> to the cache if (mCompiledQueries.size() == mMaxSqlCacheSize) { /* reached max cachesize. before adding new entry, remove an entry from the * cache. we don't want to wipe out the entire cache because of this: * GCing {@link SQLiteCompiledSql} requires call to sqlite3_finalize * JNI method. If entire cache is wiped out, it could be cause a big GC activity * just because a (rogue) process is using the cache incorrectly. */ Set<String> keySet = mCompiledQueries.keySet(); for (String s : keySet) { mCompiledQueries.remove(s); break; } } compiledSql = new SQLiteCompiledSql(this, sql); mCompiledQueries.put(sql, compiledSql); } if (SQLiteDebug.DEBUG_SQL_CACHE) { Log.v(TAG, "|adding_sql_to_cache|" + getPath() + "|" + mCompiledQueries.size() + "|" + sql); } return true; } /** * 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) { if (mMaxSqlCacheSize == 0) { // for this database, there is no cache of compiled sql. if (SQLiteDebug.DEBUG_SQL_CACHE) { Log.v(TAG, "|cache NOT found|" + getPath()); } return null; } 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 + "|" + mTimeOpened + "|" + mTimeClosed + "|" + sql); } return compiledStatement; } /* package */ void logTimeStat(boolean read, long begin, long end) { EventLog.writeEvent(EVENT_DB_OPERATION, mPath, read ? 0 : 1, end - begin); } Loading
core/java/android/database/sqlite/SQLiteDebug.java +6 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,12 @@ public final class SQLiteDebug { public static final boolean DEBUG_SQL_STATEMENTS = Log.isLoggable("SQLiteStatements", Log.VERBOSE); /** * Controls the printing of compiled-sql-statement cache stats. */ public static final boolean DEBUG_SQL_CACHE = Log.isLoggable("SQLiteCompiledSql", Log.VERBOSE); /** * Controls the stack trace reporting of active cursors being * finalized. Loading
core/java/android/database/sqlite/SQLiteProgram.java +55 −70 Original line number Diff line number Diff line Loading @@ -27,6 +27,9 @@ public abstract class SQLiteProgram extends SQLiteClosable { /** The database this program is compiled against. */ protected SQLiteDatabase mDatabase; /** The SQL used to create this query */ /* package */ final String mSql; /** * Native linkage, do not modify. This comes from the database and should not be modified * in here or in the native code. Loading @@ -34,45 +37,60 @@ public abstract class SQLiteProgram extends SQLiteClosable { protected int nHandle = 0; /** * Native linkage, do not modify. When non-0 this holds a reference to a valid * sqlite3_statement object. It is only updated by the native code, but may be * checked in this class when the database lock is held to determine if there * is a valid native-side program or not. * the compiledSql object for the given sql statement. */ protected int nStatement = 0; private SQLiteCompiledSql compiledSql; private boolean myCompiledSqlIsInCache; /** * Used to find out where a cursor was allocated in case it never got * released. * compiledSql statement id is populated with the corresponding object from the above * member compiledSql. * this member is used by the native_bind_* methods */ private StackTraceElement[] mStackTraceElements; protected int nStatement = 0; /* package */ SQLiteProgram(SQLiteDatabase db, String sql) { if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { mStackTraceElements = new Exception().getStackTrace(); Log.d(TAG, "processing sql: " + sql); } mDatabase = db; mSql = sql; db.acquireReference(); db.addSQLiteClosable(this); this.nHandle = db.mNativeHandle; compile(sql, false); compiledSql = db.getCompiledStatementForSql(sql); if (compiledSql == null) { // create a new compiled-sql obj compiledSql = new SQLiteCompiledSql(db, sql); // add it to the cache of compiled-sqls myCompiledSqlIsInCache = db.addToCompiledQueries(sql, compiledSql); } else { myCompiledSqlIsInCache = true; } nStatement = compiledSql.nStatement; } @Override protected void onAllReferencesReleased() { // Note that native_finalize() checks to make sure that nStatement is // non-null before destroying it. native_finalize(); // release the compiled sql statement used by me if it is NOT in cache if (!myCompiledSqlIsInCache) { compiledSql.releaseSqlStatement(); compiledSql = null; // so that GC doesn't call finalize() on it } mDatabase.releaseReference(); mDatabase.removeSQLiteClosable(this); } @Override protected void onAllReferencesReleasedFromContainer() { // Note that native_finalize() checks to make sure that nStatement is // non-null before destroying it. native_finalize(); // release the compiled sql statement used by me if it is NOT in cache if (!myCompiledSqlIsInCache) { compiledSql.releaseSqlStatement(); compiledSql = null; // so that GC doesn't call finalize() on it } mDatabase.releaseReference(); } Loading @@ -82,37 +100,23 @@ public abstract class SQLiteProgram extends SQLiteClosable { * @return a unique identifier for this program */ public final int getUniqueId() { return nStatement; return compiledSql.nStatement; } /* package */ String getSqlString() { return mSql; } /** * 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> * @deprecated use this.compiledStatement.compile instead * * @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 */ @Deprecated protected void compile(String sql, boolean forceCompilation) { // Only compile if we don't have a valid statement already or the caller has // explicitly requested a recompile. if (nStatement == 0 || forceCompilation) { mDatabase.lock(); try { // Note that the native_compile() takes care of destroying any previously // existing programs before it compiles. acquireReference(); native_compile(sql); } finally { releaseReference(); mDatabase.unlock(); } } // TODO is there a need for this? } /** Loading Loading @@ -224,34 +228,15 @@ public abstract class SQLiteProgram extends SQLiteClosable { } } /** * Make sure that the native resource is cleaned up. */ @Override protected void finalize() { if (nStatement != 0) { if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { String message = "Finalizing " + this + " that has not been closed"; Log.d(TAG, message + "\nThis cursor was created in:"); for (StackTraceElement ste : mStackTraceElements) { Log.d(TAG, " " + ste); } } // when in finalize() it is already removed from weakhashmap // so it is safe to not removed itself from db onAllReferencesReleasedFromContainer(); } } /** * Compiles SQL into a SQLite program. * * <P>The database lock must be held when calling this method. * @param sql The SQL to compile. */ @Deprecated protected final native void native_compile(String sql); @Deprecated protected final native void native_finalize(); protected final native void native_bind_null(int index); Loading