summaryrefslogtreecommitdiffstats
path: root/core/java/android/database
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:05:43 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:05:43 -0800
commitf013e1afd1e68af5e3b868c26a653bbfb39538f8 (patch)
tree7ad6c8fd9c7b55f4b4017171dec1cb760bbd26bf /core/java/android/database
parente70cfafe580c6f2994c4827cd8a534aabf3eb05c (diff)
downloadframeworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.zip
frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.tar.gz
frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.tar.bz2
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'core/java/android/database')
-rw-r--r--core/java/android/database/AbstractCursor.java21
-rw-r--r--core/java/android/database/AbstractWindowedCursor.java2
-rw-r--r--core/java/android/database/CursorWindow.java9
-rw-r--r--core/java/android/database/DatabaseUtils.java15
-rw-r--r--core/java/android/database/sqlite/SQLiteCursor.java174
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java168
-rw-r--r--core/java/android/database/sqlite/SQLiteOpenHelper.java2
-rw-r--r--core/java/android/database/sqlite/SQLiteProgram.java4
-rw-r--r--core/java/android/database/sqlite/SQLiteQuery.java56
9 files changed, 399 insertions, 52 deletions
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index e81f7f8..76f0860 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -21,6 +21,10 @@ import android.net.Uri;
import android.util.Config;
import android.util.Log;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
import java.lang.ref.WeakReference;
import java.lang.UnsupportedOperationException;
@@ -457,9 +461,24 @@ public abstract class AbstractCursor implements CrossProcessCursor {
mContentObservable.unregisterObserver(observer);
}
}
-
+
+ /**
+ * @hide pending API council approval
+ */
+ protected void notifyDataSetChange() {
+ mDataSetObservable.notifyChanged();
+ }
+
+ /**
+ * @hide pending API council approval
+ */
+ protected DataSetObservable getDataSetObservable() {
+ return mDataSetObservable;
+
+ }
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
+
}
public void unregisterDataSetObserver(DataSetObserver observer) {
diff --git a/core/java/android/database/AbstractWindowedCursor.java b/core/java/android/database/AbstractWindowedCursor.java
index 1ec4312..4ac0aef 100644
--- a/core/java/android/database/AbstractWindowedCursor.java
+++ b/core/java/android/database/AbstractWindowedCursor.java
@@ -172,7 +172,7 @@ public abstract class AbstractWindowedCursor extends AbstractCursor
super.checkPosition();
if (mWindow == null) {
- throw new StaleDataException("This cursor has changed, you must call requery()");
+ throw new StaleDataException("Access closed cursor");
}
}
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 72dc3a9..8e26730 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -409,8 +409,13 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
* change across a call to clear().
*/
public void clear() {
- mStartPos = 0;
- native_clear();
+ acquireReference();
+ try {
+ mStartPos = 0;
+ native_clear();
+ } finally {
+ releaseReference();
+ }
}
/** Clears out the native side of things */
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index ab0dc3f..2ff7294 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -241,6 +241,21 @@ public class DatabaseUtils {
}
/**
+ * Concatenates two SQL WHERE clauses, handling empty or null values.
+ * @hide
+ */
+ public static String concatenateWhere(String a, String b) {
+ if (TextUtils.isEmpty(a)) {
+ return b;
+ }
+ if (TextUtils.isEmpty(b)) {
+ return a;
+ }
+
+ return "(" + a + ") AND (" + b + ")";
+ }
+
+ /**
* return the collation key
* @param name
* @return the collation key
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index ae2fc95..70b9b83 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -18,7 +18,12 @@ package android.database.sqlite;
import android.database.AbstractWindowedCursor;
import android.database.CursorWindow;
+import android.database.DataSetObserver;
import android.database.SQLException;
+
+import android.os.Handler;
+import android.os.Message;
+import android.os.Process;
import android.text.TextUtils;
import android.util.Config;
import android.util.Log;
@@ -26,6 +31,7 @@ import android.util.Log;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.concurrent.locks.ReentrantLock;
/**
* A Cursor implementation that exposes results from a query on a
@@ -59,7 +65,131 @@ public class SQLiteCursor extends AbstractWindowedCursor {
/** Used to find out where a cursor was allocated in case it never got
* released. */
private StackTraceElement[] mStackTraceElements;
-
+
+ /**
+ * mMaxRead is the max items that each cursor window reads
+ * default to a very high value
+ */
+ private int mMaxRead = Integer.MAX_VALUE;
+ private int mInitialRead = Integer.MAX_VALUE;
+ private int mCursorState = 0;
+ private ReentrantLock mLock = null;
+ private boolean mPendingData = false;
+
+ /**
+ * support for a cursor variant that doesn't always read all results
+ * initialRead is the initial number of items that cursor window reads
+ * if query contains more than this number of items, a thread will be
+ * created and handle the left over items so that caller can show
+ * results as soon as possible
+ * @param initialRead initial number of items that cursor read
+ * @param maxRead leftover items read at maxRead items per time
+ * @hide
+ */
+ public void setLoadStyle(int initialRead, int maxRead) {
+ mMaxRead = maxRead;
+ mInitialRead = initialRead;
+ mLock = new ReentrantLock(true);
+ }
+
+ private void queryThreadLock() {
+ if (mLock != null) {
+ mLock.lock();
+ }
+ }
+
+ private void queryThreadUnlock() {
+ if (mLock != null) {
+ mLock.unlock();
+ }
+ }
+
+
+ /**
+ * @hide
+ */
+ final private class QueryThread implements Runnable {
+ private final int mThreadState;
+ QueryThread(int version) {
+ mThreadState = version;
+ }
+ private void sendMessage() {
+ if (mNotificationHandler != null) {
+ mNotificationHandler.sendEmptyMessage(1);
+ mPendingData = false;
+ } else {
+ mPendingData = true;
+ }
+
+ }
+ public void run() {
+ // use cached mWindow, to avoid get null mWindow
+ CursorWindow cw = mWindow;
+ Process.setThreadPriority(Process.myTid(), Process.THREAD_PRIORITY_BACKGROUND);
+ // the cursor's state doesn't change
+ while (true) {
+ mLock.lock();
+ if (mCursorState != mThreadState) {
+ mLock.unlock();
+ break;
+ }
+ try {
+ int count = mQuery.fillWindow(cw, mMaxRead, mCount);
+ // return -1 means not finished
+ if (count != 0) {
+ if (count == NO_COUNT){
+ mCount += mMaxRead;
+ sendMessage();
+ } else {
+ mCount = count;
+ sendMessage();
+ break;
+ }
+ } else {
+ break;
+ }
+ } catch (Exception e) {
+ // end the tread when the cursor is close
+ break;
+ } finally {
+ mLock.unlock();
+ }
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ protected class MainThreadNotificationHandler extends Handler {
+ public void handleMessage(Message msg) {
+ notifyDataSetChange();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ protected MainThreadNotificationHandler mNotificationHandler;
+
+ public void registerDataSetObserver(DataSetObserver observer) {
+ super.registerDataSetObserver(observer);
+ if ((Integer.MAX_VALUE != mMaxRead || Integer.MAX_VALUE != mInitialRead) &&
+ mNotificationHandler == null) {
+ queryThreadLock();
+ try {
+ mNotificationHandler = new MainThreadNotificationHandler();
+ if (mPendingData) {
+ notifyDataSetChange();
+ mPendingData = false;
+ }
+ } finally {
+ queryThreadUnlock();
+ }
+ }
+
+ }
+
/**
* Execute a query and provide access to its result set through a Cursor
* interface. For a query such as: {@code SELECT name, birth, phone FROM
@@ -146,11 +276,22 @@ public class SQLiteCursor extends AbstractWindowedCursor {
// If there isn't a window set already it will only be accessed locally
mWindow = new CursorWindow(true /* the window is local only */);
} else {
- mWindow.clear();
+ mCursorState++;
+ queryThreadLock();
+ try {
+ mWindow.clear();
+ } finally {
+ queryThreadUnlock();
+ }
}
-
- // mWindow must be cleared
- mCount = mQuery.fillWindow(mWindow, startPos);
+ mWindow.setStartPosition(startPos);
+ mCount = mQuery.fillWindow(mWindow, mInitialRead, 0);
+ // return -1 means not finished
+ if (mCount == NO_COUNT){
+ mCount = startPos + mInitialRead;
+ Thread t = new Thread(new QueryThread(mCursorState), "query thread");
+ t.start();
+ }
}
@Override
@@ -344,6 +485,7 @@ public class SQLiteCursor extends AbstractWindowedCursor {
private void deactivateCommon() {
if (Config.LOGV) Log.v(TAG, "<<< Releasing cursor " + this);
+ mCursorState = 0;
if (mWindow != null) {
mWindow.close();
mWindow = null;
@@ -368,6 +510,9 @@ public class SQLiteCursor extends AbstractWindowedCursor {
@Override
public boolean requery() {
+ if (isClosed()) {
+ return false;
+ }
long timeStart = 0;
if (Config.LOGV) {
timeStart = System.currentTimeMillis();
@@ -385,8 +530,13 @@ public class SQLiteCursor extends AbstractWindowedCursor {
// This one will recreate the temp table, and get its count
mDriver.cursorRequeried(this);
mCount = NO_COUNT;
- // Requery the program that runs over the temp table
- mQuery.requery();
+ mCursorState++;
+ queryThreadLock();
+ try {
+ mQuery.requery();
+ } finally {
+ queryThreadUnlock();
+ }
} finally {
mDatabase.unlock();
}
@@ -405,9 +555,15 @@ public class SQLiteCursor extends AbstractWindowedCursor {
}
@Override
- public void setWindow(CursorWindow window) {
+ public void setWindow(CursorWindow window) {
if (mWindow != null) {
- mWindow.close();
+ mCursorState++;
+ queryThreadLock();
+ try {
+ mWindow.close();
+ } finally {
+ queryThreadUnlock();
+ }
mCount = NO_COUNT;
}
mWindow = window;
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index e497190..fa062c8 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -54,6 +54,69 @@ public class SQLiteDatabase extends SQLiteClosable {
private final static String TAG = "Database";
/**
+ * Algorithms used in ON CONFLICT clause
+ * http://www.sqlite.org/lang_conflict.html
+ * @hide
+ */
+ public enum ConflictAlgorithm {
+ /**
+ * When a constraint violation occurs, an immediate ROLLBACK occurs,
+ * thus ending the current transaction, and the command aborts with a
+ * return code of SQLITE_CONSTRAINT. If no transaction is active
+ * (other than the implied transaction that is created on every command)
+ * then this algorithm works the same as ABORT.
+ */
+ ROLLBACK("ROLLBACK"),
+
+ /**
+ * When a constraint violation occurs,no ROLLBACK is executed
+ * so changes from prior commands within the same transaction
+ * are preserved. This is the default behavior.
+ */
+ ABORT("ABORT"),
+
+ /**
+ * When a constraint violation occurs, the command aborts with a return
+ * code SQLITE_CONSTRAINT. But any changes to the database that
+ * the command made prior to encountering the constraint violation
+ * are preserved and are not backed out.
+ */
+ FAIL("FAIL"),
+
+ /**
+ * When a constraint violation occurs, the one row that contains
+ * the constraint violation is not inserted or changed.
+ * But the command continues executing normally. Other rows before and
+ * after the row that contained the constraint violation continue to be
+ * inserted or updated normally. No error is returned.
+ */
+ IGNORE("IGNORE"),
+
+ /**
+ * When a UNIQUE constraint violation occurs, the pre-existing rows that
+ * are causing the constraint violation are removed prior to inserting
+ * or updating the current row. Thus the insert or update always occurs.
+ * The command continues executing normally. No error is returned.
+ * If a NOT NULL constraint violation occurs, the NULL value is replaced
+ * by the default value for that column. If the column has no default
+ * value, then the ABORT algorithm is used. If a CHECK constraint
+ * violation occurs then the IGNORE algorithm is used. When this conflict
+ * resolution strategy deletes rows in order to satisfy a constraint,
+ * it does not invoke delete triggers on those rows.
+ * This behavior might change in a future release.
+ */
+ REPLACE("REPLACE");
+
+ private final String mValue;
+ ConflictAlgorithm(String value) {
+ mValue = value;
+ }
+ public String value() {
+ return mValue;
+ }
+ }
+
+ /**
* Maximum Length Of A LIKE Or GLOB Pattern
* The pattern matching algorithm used in the default LIKE and GLOB implementation
* of SQLite can exhibit O(N^2) performance (where N is the number of characters in
@@ -437,8 +500,26 @@ public class SQLiteDatabase extends SQLiteClosable {
* successful so far. Do not call setTransactionSuccessful before calling this. When this
* returns a new transaction will have been created but not marked as successful.
* @return true if the transaction was yielded
+ * @deprecated if the db is locked more than once (becuase of nested transactions) then the lock
+ * will not be yielded. Use yieldIfContendedSafely instead.
*/
public boolean yieldIfContended() {
+ return yieldIfContendedHelper(false /* do not check yielding */);
+ }
+
+ /**
+ * Temporarily end the transaction to let other threads run. The transaction is assumed to be
+ * successful so far. Do not call setTransactionSuccessful before calling this. When this
+ * returns a new transaction will have been created but not marked as successful. This assumes
+ * that there are no nested transactions (beginTransaction has only been called once) and will
+ * through an exception if that is not the case.
+ * @return true if the transaction was yielded
+ */
+ public boolean yieldIfContendedSafely() {
+ return yieldIfContendedHelper(true /* check yielding */);
+ }
+
+ private boolean yieldIfContendedHelper(boolean checkFullyYielded) {
if (mLock.getQueueLength() == 0) {
// Reset the lock acquire time since we know that the thread was willing to yield
// the lock at this time.
@@ -448,6 +529,12 @@ public class SQLiteDatabase extends SQLiteClosable {
}
setTransactionSuccessful();
endTransaction();
+ if (checkFullyYielded) {
+ if (this.isDbLockedByCurrentThread()) {
+ throw new IllegalStateException(
+ "Db locked more than once. yielfIfContended cannot yield");
+ }
+ }
beginTransaction();
return true;
}
@@ -1031,6 +1118,28 @@ public class SQLiteDatabase extends SQLiteClosable {
}
/**
+ * Runs the provided SQL and returns a cursor over the result set.
+ * The cursor will read an initial set of rows and the return to the caller.
+ * It will continue to read in batches and send data changed notifications
+ * when the later batches are ready.
+ * @param sql the SQL query. The SQL string must not be ; terminated
+ * @param selectionArgs You may include ?s in where clause in the query,
+ * which will be replaced by the values from selectionArgs. The
+ * values will be bound as Strings.
+ * @param initialRead set the initial count of items to read from the cursor
+ * @param maxRead set the count of items to read on each iteration after the first
+ * @return A {@link Cursor} object, which is positioned before the first entry
+ * @hide pending API council approval
+ */
+ public Cursor rawQuery(String sql, String[] selectionArgs,
+ int initialRead, int maxRead) {
+ SQLiteCursor c = (SQLiteCursor)rawQueryWithFactory(
+ null, sql, selectionArgs, null);
+ c.setLoadStyle(initialRead, maxRead);
+ return c;
+ }
+
+ /**
* Convenience method for inserting a row into the database.
*
* @param table the table to insert the row into
@@ -1044,7 +1153,7 @@ public class SQLiteDatabase extends SQLiteClosable {
*/
public long insert(String table, String nullColumnHack, ContentValues values) {
try {
- return insertOrReplace(table, nullColumnHack, values, false);
+ return insertWithOnConflict(table, nullColumnHack, values, null);
} catch (SQLException e) {
Log.e(TAG, "Error inserting " + values, e);
return -1;
@@ -1066,7 +1175,7 @@ public class SQLiteDatabase extends SQLiteClosable {
*/
public long insertOrThrow(String table, String nullColumnHack, ContentValues values)
throws SQLException {
- return insertOrReplace(table, nullColumnHack, values, false) ;
+ return insertWithOnConflict(table, nullColumnHack, values, null);
}
/**
@@ -1082,7 +1191,8 @@ public class SQLiteDatabase extends SQLiteClosable {
*/
public long replace(String table, String nullColumnHack, ContentValues initialValues) {
try {
- return insertOrReplace(table, nullColumnHack, initialValues, true);
+ return insertWithOnConflict(table, nullColumnHack, initialValues,
+ ConflictAlgorithm.REPLACE);
} catch (SQLException e) {
Log.e(TAG, "Error inserting " + initialValues, e);
return -1;
@@ -1103,22 +1213,38 @@ public class SQLiteDatabase extends SQLiteClosable {
*/
public long replaceOrThrow(String table, String nullColumnHack,
ContentValues initialValues) throws SQLException {
- return insertOrReplace(table, nullColumnHack, initialValues, true);
+ return insertWithOnConflict(table, nullColumnHack, initialValues,
+ ConflictAlgorithm.REPLACE);
}
- private long insertOrReplace(String table, String nullColumnHack,
- ContentValues initialValues, boolean allowReplace) {
+ /**
+ * General method for inserting a row into the database.
+ *
+ * @param table the table to insert the row into
+ * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
+ * so if initialValues is empty this column will explicitly be
+ * assigned a NULL value
+ * @param initialValues this map contains the initial column values for the
+ * row. The keys should be the column names and the values the
+ * column values
+ * @param algorithm {@link ConflictAlgorithm} for insert conflict resolver
+ * @return the row ID of the newly inserted row, or -1 if an error occurred
+ * @hide
+ */
+ public long insertWithOnConflict(String table, String nullColumnHack,
+ ContentValues initialValues, ConflictAlgorithm algorithm) {
if (!isOpen()) {
throw new IllegalStateException("database not open");
}
// Measurements show most sql lengths <= 152
StringBuilder sql = new StringBuilder(152);
- sql.append("INSERT ");
- if (allowReplace) {
- sql.append("OR REPLACE ");
+ sql.append("INSERT");
+ if (algorithm != null) {
+ sql.append(" OR ");
+ sql.append(algorithm.value());
}
- sql.append("INTO ");
+ sql.append(" INTO ");
sql.append(table);
// Measurements show most values lengths < 40
StringBuilder values = new StringBuilder(40);
@@ -1241,6 +1367,23 @@ public class SQLiteDatabase extends SQLiteClosable {
* @return the number of rows affected
*/
public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
+ return updateWithOnConflict(table, values, whereClause, whereArgs, null);
+ }
+
+ /**
+ * Convenience method for updating rows in the database.
+ *
+ * @param table the table to update in
+ * @param values a map from column names to new column values. null is a
+ * valid value that will be translated to NULL.
+ * @param whereClause the optional WHERE clause to apply when updating.
+ * Passing null will update all rows.
+ * @param algorithm {@link ConflictAlgorithm} for update conflict resolver
+ * @return the number of rows affected
+ * @hide
+ */
+ public int updateWithOnConflict(String table, ContentValues values,
+ String whereClause, String[] whereArgs, ConflictAlgorithm algorithm) {
if (!isOpen()) {
throw new IllegalStateException("database not open");
}
@@ -1251,6 +1394,11 @@ public class SQLiteDatabase extends SQLiteClosable {
StringBuilder sql = new StringBuilder(120);
sql.append("UPDATE ");
+ if (algorithm != null) {
+ sql.append(" OR ");
+ sql.append(algorithm.value());
+ }
+
sql.append(table);
sql.append(" SET ");
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index f6872ac..35bf645 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -26,8 +26,6 @@ import android.util.Log;
* optionally {@link #onOpen}, and this class takes care of opening the database
* if it exists, creating it if it does not, and upgrading it as necessary.
* Transactions are used to make sure the database is always in a sensible state.
- *
- * @see com.google.provider.NotePad.NotePadProvider
*/
public abstract class SQLiteOpenHelper {
private static final String TAG = SQLiteOpenHelper.class.getSimpleName();
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index e0341a2..f89c87d 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -239,7 +239,9 @@ public abstract class SQLiteProgram extends SQLiteClosable {
Log.d(TAG, " " + ste);
}
}
- onAllReferencesReleased();
+ // when in finalize() it is already removed from weakhashmap
+ // so it is safe to not removed itself from db
+ onAllReferencesReleasedFromContainer();
}
}
diff --git a/core/java/android/database/sqlite/SQLiteQuery.java b/core/java/android/database/sqlite/SQLiteQuery.java
index 40855b6..22c53ab 100644
--- a/core/java/android/database/sqlite/SQLiteQuery.java
+++ b/core/java/android/database/sqlite/SQLiteQuery.java
@@ -40,9 +40,8 @@ public class SQLiteQuery extends SQLiteProgram {
* Create a persistent query object.
*
* @param db The database that this query object is associated with
- * @param query The SQL string for this query. It must include "INDEX -1
- * OFFSET ?" at the end
- * @param offsetIndex The 1-based index to the OFFSET parameter
+ * @param query The SQL string for this query.
+ * @param offsetIndex The 1-based index to the OFFSET parameter,
*/
/* package */ SQLiteQuery(SQLiteDatabase db, String query, int offsetIndex, String[] bindArgs) {
super(db, query);
@@ -59,24 +58,28 @@ public class SQLiteQuery extends SQLiteProgram {
* @param startPos The position to start reading rows from
* @return number of total rows in the query
*/
- /* package */ int fillWindow(CursorWindow window, int startPos) {
- if (startPos < 0) {
- throw new IllegalArgumentException("startPos should > 0");
- }
- window.setStartPosition(startPos);
+ /* package */ int fillWindow(CursorWindow window,
+ int maxRead, int lastPos) {
mDatabase.lock();
try {
acquireReference();
- window.acquireReference();
- return native_fill_window(window, startPos, mOffsetIndex);
- } catch (IllegalStateException e){
- // simply ignore it
- return 0;
- } catch (SQLiteDatabaseCorruptException e) {
- mDatabase.onCorruption();
- throw e;
+ try {
+ window.acquireReference();
+ // if the start pos is not equal to 0, then most likely window is
+ // too small for the data set, loading by another thread
+ // is not safe in this situation. the native code will ignore maxRead
+ return native_fill_window(window, window.getStartPosition(), mOffsetIndex,
+ maxRead, lastPos);
+ } catch (IllegalStateException e){
+ // simply ignore it
+ return 0;
+ } catch (SQLiteDatabaseCorruptException e) {
+ mDatabase.onCorruption();
+ throw e;
+ } finally {
+ window.releaseReference();
+ }
} finally {
- window.releaseReference();
releaseReference();
mDatabase.unlock();
}
@@ -113,7 +116,13 @@ public class SQLiteQuery extends SQLiteProgram {
releaseReference();
}
}
-
+
+ /** {@hide pending API Council approval} */
+ @Override
+ public String toString() {
+ return "SQLiteQuery: " + mQuery;
+ }
+
@Override
public void close() {
super.close();
@@ -124,11 +133,6 @@ public class SQLiteQuery extends SQLiteProgram {
* Called by SQLiteCursor when it is requeried.
*/
/* package */ void requery() {
- boolean oldMClosed = mClosed;
- if (mClosed) {
- mClosed = false;
- compile(mQuery, false);
- }
if (mBindArgs != null) {
int len = mBindArgs.length;
try {
@@ -136,8 +140,7 @@ public class SQLiteQuery extends SQLiteProgram {
super.bindString(i + 1, mBindArgs[i]);
}
} catch (SQLiteMisuseException e) {
- StringBuilder errMsg = new StringBuilder
- ("old mClosed " + oldMClosed + " mQuery " + mQuery);
+ StringBuilder errMsg = new StringBuilder("mQuery " + mQuery);
for (int i = 0; i < len; i++) {
errMsg.append(" ");
errMsg.append(mBindArgs[i]);
@@ -174,7 +177,8 @@ public class SQLiteQuery extends SQLiteProgram {
if (!mClosed) super.bindString(index, value);
}
- private final native int native_fill_window(CursorWindow window, int startPos, int offsetParam);
+ private final native int native_fill_window(CursorWindow window,
+ int startPos, int offsetParam, int maxRead, int lastPos);
private final native int native_column_count();