summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorVasu Nori <vnori@google.com>2009-10-20 15:16:35 -0700
committerVasu Nori <vnori@google.com>2009-11-20 14:09:24 -0800
commit5a03f36ef845f73eb4473193dbb0f93dd12a51af (patch)
treed5791b62e94ca116801c545dc55fa2729b6915cf /core
parent53e9c126f5fc39459554f0290a2c863f645d397a (diff)
downloadframeworks_base-5a03f36ef845f73eb4473193dbb0f93dd12a51af.zip
frameworks_base-5a03f36ef845f73eb4473193dbb0f93dd12a51af.tar.gz
frameworks_base-5a03f36ef845f73eb4473193dbb0f93dd12a51af.tar.bz2
maintain cache of statementids returned by sqlite upon compiling a sql stmnt
Diffstat (limited to 'core')
-rw-r--r--core/java/android/database/sqlite/SQLiteCompiledSql.java111
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java157
-rw-r--r--core/java/android/database/sqlite/SQLiteDebug.java6
-rw-r--r--core/java/android/database/sqlite/SQLiteProgram.java125
-rw-r--r--core/java/android/database/sqlite/SQLiteQuery.java10
-rw-r--r--core/java/android/database/sqlite/SQLiteStatement.java9
-rw-r--r--core/jni/Android.mk1
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_database_SQLiteCompiledSql.cpp134
-rw-r--r--core/jni/android_database_SQLiteProgram.cpp65
10 files changed, 480 insertions, 140 deletions
diff --git a/core/java/android/database/sqlite/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java
new file mode 100644
index 0000000..79527b4
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java
@@ -0,0 +1,111 @@
+/*
+ * 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();
+}
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index f621483..a07176f 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -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;
@@ -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;
@@ -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
@@ -251,6 +282,9 @@ public class SQLiteDatabase extends SQLiteClosable {
@Override
protected void onAllReferencesReleased() {
if (isOpen()) {
+ if (SQLiteDebug.DEBUG_SQL_CACHE) {
+ mTimeClosed = getTime();
+ }
dbclose();
}
}
@@ -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();
+ }
+ }
}
/**
@@ -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
@@ -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);
}
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index 84d8879..d4d3059 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -32,6 +32,12 @@ public final class SQLiteDebug {
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.
*/
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 9e85452..0418324 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -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.
@@ -34,87 +37,88 @@ 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();
- mDatabase.releaseReference();
+ protected void onAllReferencesReleasedFromContainer() {
+ // 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();
}
/**
* Returns a unique identifier for this program.
- *
+ *
* @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?
+ }
+
/**
* Bind a NULL value to this statement. The value remains bound until
* {@link #clearBindings} is called.
@@ -221,37 +225,18 @@ public abstract class SQLiteProgram extends SQLiteClosable {
releaseReference();
} finally {
mDatabase.unlock();
- }
- }
-
- /**
- * 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);
diff --git a/core/java/android/database/sqlite/SQLiteQuery.java b/core/java/android/database/sqlite/SQLiteQuery.java
index cdd9f86..94b950e 100644
--- a/core/java/android/database/sqlite/SQLiteQuery.java
+++ b/core/java/android/database/sqlite/SQLiteQuery.java
@@ -30,9 +30,6 @@ public class SQLiteQuery extends SQLiteProgram {
/** The index of the unbound OFFSET parameter */
private int mOffsetIndex;
- /** The SQL used to create this query */
- private String mQuery;
-
/** Args to bind on requery */
private String[] mBindArgs;
@@ -49,7 +46,6 @@ public class SQLiteQuery extends SQLiteProgram {
super(db, query);
mOffsetIndex = offsetIndex;
- mQuery = query;
mBindArgs = bindArgs;
}
@@ -77,7 +73,7 @@ public class SQLiteQuery extends SQLiteProgram {
// Logging
if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
- Log.d(TAG, "fillWindow(): " + mQuery);
+ Log.d(TAG, "fillWindow(): " + mSql);
}
if (logStats) {
mDatabase.logTimeStat(true /* read */, startTime,
@@ -133,7 +129,7 @@ public class SQLiteQuery extends SQLiteProgram {
@Override
public String toString() {
- return "SQLiteQuery: " + mQuery;
+ return "SQLiteQuery: " + mSql;
}
@Override
@@ -153,7 +149,7 @@ public class SQLiteQuery extends SQLiteProgram {
super.bindString(i + 1, mBindArgs[i]);
}
} catch (SQLiteMisuseException e) {
- StringBuilder errMsg = new StringBuilder("mQuery " + mQuery);
+ StringBuilder errMsg = new StringBuilder("mSql " + mSql);
for (int i = 0; i < len; i++) {
errMsg.append(" ");
errMsg.append(mBindArgs[i]);
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index 5889ad9..a81c9a6 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -29,8 +29,6 @@ public class SQLiteStatement extends SQLiteProgram
{
private static final String TAG = "SQLiteStatement";
- private final String mSql;
-
/**
* Don't use SQLiteStatement constructor directly, please use
* {@link SQLiteDatabase#compileStatement(String)}
@@ -39,11 +37,6 @@ public class SQLiteStatement extends SQLiteProgram
*/
/* package */ SQLiteStatement(SQLiteDatabase db, String sql) {
super(db, sql);
- if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
- mSql = sql;
- } else {
- mSql = null;
- }
}
/**
@@ -67,7 +60,7 @@ public class SQLiteStatement extends SQLiteProgram
if (logStats) {
mDatabase.logTimeStat(false /* write */, startTime, SystemClock.elapsedRealtime());
}
- } finally {
+ } finally {
releaseReference();
mDatabase.unlock();
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 015268b..b9042f9 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -33,6 +33,7 @@ LOCAL_SRC_FILES:= \
android_opengl_GLES11.cpp \
android_opengl_GLES11Ext.cpp \
android_database_CursorWindow.cpp \
+ android_database_SQLiteCompiledSql.cpp \
android_database_SQLiteDebug.cpp \
android_database_SQLiteDatabase.cpp \
android_database_SQLiteProgram.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f61e247..d069d7d 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -115,6 +115,7 @@ extern int register_android_view_Display(JNIEnv* env);
extern int register_android_view_Surface(JNIEnv* env);
extern int register_android_view_ViewRoot(JNIEnv* env);
extern int register_android_database_CursorWindow(JNIEnv* env);
+extern int register_android_database_SQLiteCompiledSql(JNIEnv* env);
extern int register_android_database_SQLiteDatabase(JNIEnv* env);
extern int register_android_database_SQLiteDebug(JNIEnv* env);
extern int register_android_database_SQLiteProgram(JNIEnv* env);
@@ -1205,6 +1206,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_graphics_NativeUtils),
REG_JNI(register_android_database_CursorWindow),
+ REG_JNI(register_android_database_SQLiteCompiledSql),
REG_JNI(register_android_database_SQLiteDatabase),
REG_JNI(register_android_database_SQLiteDebug),
REG_JNI(register_android_database_SQLiteProgram),
diff --git a/core/jni/android_database_SQLiteCompiledSql.cpp b/core/jni/android_database_SQLiteCompiledSql.cpp
new file mode 100644
index 0000000..8d1c39e
--- /dev/null
+++ b/core/jni/android_database_SQLiteCompiledSql.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2006-2008 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "Cursor"
+
+#include <jni.h>
+#include <JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+
+#include <sqlite3.h>
+
+#include <utils/Log.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sqlite3_exception.h"
+
+
+namespace android {
+
+static jfieldID gHandleField;
+static jfieldID gStatementField;
+
+
+#define GET_STATEMENT(env, object) \
+ (sqlite3_stmt *)env->GetIntField(object, gStatementField)
+#define GET_HANDLE(env, object) \
+ (sqlite3 *)env->GetIntField(object, gHandleField)
+
+
+sqlite3_stmt * compile(JNIEnv* env, jobject object,
+ sqlite3 * handle, jstring sqlString)
+{
+ int err;
+ jchar const * sql;
+ jsize sqlLen;
+ sqlite3_stmt * statement = GET_STATEMENT(env, object);
+
+ // Make sure not to leak the statement if it already exists
+ if (statement != NULL) {
+ sqlite3_finalize(statement);
+ env->SetIntField(object, gStatementField, 0);
+ }
+
+ // Compile the SQL
+ sql = env->GetStringChars(sqlString, NULL);
+ sqlLen = env->GetStringLength(sqlString);
+ err = sqlite3_prepare16_v2(handle, sql, sqlLen * 2, &statement, NULL);
+ env->ReleaseStringChars(sqlString, sql);
+
+ if (err == SQLITE_OK) {
+ // Store the statement in the Java object for future calls
+ LOGV("Prepared statement %p on %p", statement, handle);
+ env->SetIntField(object, gStatementField, (int)statement);
+ return statement;
+ } else {
+ // Error messages like 'near ")": syntax error' are not
+ // always helpful enough, so construct an error string that
+ // includes the query itself.
+ const char *query = env->GetStringUTFChars(sqlString, NULL);
+ char *message = (char*) malloc(strlen(query) + 50);
+ if (message) {
+ strcpy(message, ", while compiling: "); // less than 50 chars
+ strcat(message, query);
+ }
+ env->ReleaseStringUTFChars(sqlString, query);
+ throw_sqlite3_exception(env, handle, message);
+ free(message);
+ return NULL;
+ }
+}
+
+static void native_compile(JNIEnv* env, jobject object, jstring sqlString)
+{
+ compile(env, object, GET_HANDLE(env, object), sqlString);
+}
+
+static void native_finalize(JNIEnv* env, jobject object)
+{
+ int err;
+ sqlite3_stmt * statement = GET_STATEMENT(env, object);
+
+ if (statement != NULL) {
+ sqlite3_finalize(statement);
+ env->SetIntField(object, gStatementField, 0);
+ }
+}
+
+static JNINativeMethod sMethods[] =
+{
+ /* name, signature, funcPtr */
+ {"native_compile", "(Ljava/lang/String;)V", (void *)native_compile},
+ {"native_finalize", "()V", (void *)native_finalize},
+};
+
+int register_android_database_SQLiteCompiledSql(JNIEnv * env)
+{
+ jclass clazz;
+
+ clazz = env->FindClass("android/database/sqlite/SQLiteCompiledSql");
+ if (clazz == NULL) {
+ LOGE("Can't find android/database/sqlite/SQLiteCompiledSql");
+ return -1;
+ }
+
+ gHandleField = env->GetFieldID(clazz, "nHandle", "I");
+ gStatementField = env->GetFieldID(clazz, "nStatement", "I");
+
+ if (gHandleField == NULL || gStatementField == NULL) {
+ LOGE("Error locating fields");
+ return -1;
+ }
+
+ return AndroidRuntime::registerNativeMethods(env,
+ "android/database/sqlite/SQLiteCompiledSql", sMethods, NELEM(sMethods));
+}
+
+} // namespace android
diff --git a/core/jni/android_database_SQLiteProgram.cpp b/core/jni/android_database_SQLiteProgram.cpp
index 7bda004..32018eb 100644
--- a/core/jni/android_database_SQLiteProgram.cpp
+++ b/core/jni/android_database_SQLiteProgram.cpp
@@ -43,52 +43,12 @@ static jfieldID gStatementField;
#define GET_HANDLE(env, object) \
(sqlite3 *)env->GetIntField(object, gHandleField)
-
-sqlite3_stmt * compile(JNIEnv* env, jobject object,
- sqlite3 * handle, jstring sqlString)
-{
- int err;
- jchar const * sql;
- jsize sqlLen;
- sqlite3_stmt * statement = GET_STATEMENT(env, object);
-
- // Make sure not to leak the statement if it already exists
- if (statement != NULL) {
- sqlite3_finalize(statement);
- env->SetIntField(object, gStatementField, 0);
- }
-
- // Compile the SQL
- sql = env->GetStringChars(sqlString, NULL);
- sqlLen = env->GetStringLength(sqlString);
- err = sqlite3_prepare16_v2(handle, sql, sqlLen * 2, &statement, NULL);
- env->ReleaseStringChars(sqlString, sql);
-
- if (err == SQLITE_OK) {
- // Store the statement in the Java object for future calls
- LOGV("Prepared statement %p on %p", statement, handle);
- env->SetIntField(object, gStatementField, (int)statement);
- return statement;
- } else {
- // Error messages like 'near ")": syntax error' are not
- // always helpful enough, so construct an error string that
- // includes the query itself.
- const char *query = env->GetStringUTFChars(sqlString, NULL);
- char *message = (char*) malloc(strlen(query) + 50);
- if (message) {
- strcpy(message, ", while compiling: "); // less than 50 chars
- strcat(message, query);
- }
- env->ReleaseStringUTFChars(sqlString, query);
- throw_sqlite3_exception(env, handle, message);
- free(message);
- return NULL;
- }
-}
-
static void native_compile(JNIEnv* env, jobject object, jstring sqlString)
{
- compile(env, object, GET_HANDLE(env, object), sqlString);
+ char buf[32];
+ sprintf(buf, "android_database_SQLiteProgram->native_compile() not implemented");
+ throw_sqlite3_exception(env, GET_HANDLE(env, object), buf);
+ return;
}
static void native_bind_null(JNIEnv* env, jobject object,
@@ -164,7 +124,7 @@ static void native_bind_blob(JNIEnv* env, jobject object,
jsize sqlLen;
sqlite3_stmt * statement= GET_STATEMENT(env, object);
- jint len = env->GetArrayLength(value);
+ jint len = env->GetArrayLength(value);
jbyte * bytes = env->GetByteArrayElements(value, NULL);
err = sqlite3_bind_blob(statement, index, bytes, len, SQLITE_TRANSIENT);
@@ -192,27 +152,22 @@ static void native_clear_bindings(JNIEnv* env, jobject object)
static void native_finalize(JNIEnv* env, jobject object)
{
- int err;
- sqlite3_stmt * statement = GET_STATEMENT(env, object);
-
- if (statement != NULL) {
- sqlite3_finalize(statement);
- env->SetIntField(object, gStatementField, 0);
- }
+ char buf[32];
+ sprintf(buf, "android_database_SQLiteProgram->native_finalize() not implemented");
+ throw_sqlite3_exception(env, GET_HANDLE(env, object), buf);
+ return;
}
static JNINativeMethod sMethods[] =
{
/* name, signature, funcPtr */
- {"native_compile", "(Ljava/lang/String;)V", (void *)native_compile},
{"native_bind_null", "(I)V", (void *)native_bind_null},
{"native_bind_long", "(IJ)V", (void *)native_bind_long},
{"native_bind_double", "(ID)V", (void *)native_bind_double},
{"native_bind_string", "(ILjava/lang/String;)V", (void *)native_bind_string},
- {"native_bind_blob", "(I[B)V", (void *)native_bind_blob},
+ {"native_bind_blob", "(I[B)V", (void *)native_bind_blob},
{"native_clear_bindings", "()V", (void *)native_clear_bindings},
- {"native_finalize", "()V", (void *)native_finalize},
};
int register_android_database_SQLiteProgram(JNIEnv * env)