ADDED import_from_base.sh Index: import_from_base.sh ================================================================== --- /dev/null +++ import_from_base.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -e + +if [[ $# -ne 1 ]] ; then + echo "$0 " + exit -1 +fi +BASE=/home/dan/tmp/git/base/ + +FILES=`find sqlite3/src/main/java/ -name *.java | grep -v CloseGuard` +FILES+=" " +FILES+=`find sqlite3/src/main/jni/ -name android_database*.cpp` +FILES+=" " +FILES+=`find sqlite3/src/main/jni/ -name android_database*.h` + +for f in $FILES ; do + B=`basename $f` + echo "Copying $B..." + cp `find $BASE -name $B` $f +done + Index: sqlite3/src/main/java/org/sqlite/database/DatabaseErrorHandler.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/DatabaseErrorHandler.java +++ sqlite3/src/main/java/org/sqlite/database/DatabaseErrorHandler.java @@ -11,27 +11,22 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ - -package org.sqlite.database; - -import org.sqlite.database.sqlite.SQLiteDatabase; + +package android.database; + +import android.database.sqlite.SQLiteDatabase; /** - * An interface to let the apps define the actions to take when the following errors are detected - * database corruption + * An interface to let apps define an action to take when database corruption is detected. */ public interface DatabaseErrorHandler { /** - * defines the method to be invoked when database corruption is detected. + * The method invoked when database corruption is detected. * @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption * is detected. */ void onCorruption(SQLiteDatabase dbObj); } Index: sqlite3/src/main/java/org/sqlite/database/DatabaseUtils.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/DatabaseUtils.java +++ sqlite3/src/main/java/org/sqlite/database/DatabaseUtils.java @@ -12,35 +12,30 @@ * 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 org.sqlite.database; +package android.database; import android.content.ContentValues; import android.content.Context; import android.content.OperationApplicationException; -import org.sqlite.database.sqlite.SQLiteAbortException; -import org.sqlite.database.sqlite.SQLiteConstraintException; -import org.sqlite.database.sqlite.SQLiteDatabase; -import org.sqlite.database.sqlite.SQLiteDatabaseCorruptException; -import org.sqlite.database.sqlite.SQLiteDiskIOException; -import org.sqlite.database.sqlite.SQLiteException; -import org.sqlite.database.sqlite.SQLiteFullException; -import org.sqlite.database.sqlite.SQLiteProgram; -import org.sqlite.database.sqlite.SQLiteStatement; - -import android.database.CursorWindow; +import android.database.sqlite.SQLiteAbortException; +import android.database.sqlite.SQLiteConstraintException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteDatabaseCorruptException; +import android.database.sqlite.SQLiteDiskIOException; +import android.database.sqlite.SQLiteException; +import android.database.sqlite.SQLiteFullException; +import android.database.sqlite.SQLiteProgram; +import android.database.sqlite.SQLiteStatement; import android.os.OperationCanceledException; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.text.TextUtils; import android.util.Log; -import android.database.Cursor; - -import java.io.File; import java.io.FileNotFoundException; import java.io.PrintStream; import java.text.Collator; import java.util.HashMap; import java.util.Locale; @@ -131,40 +126,40 @@ * otherwise return and let you read the normal result data from the Parcel. * @param reply Parcel to read from * @see Parcel#writeNoException * @see Parcel#readException */ -// public static final void readExceptionFromParcel(Parcel reply) { -// int code = reply.readExceptionCode(); -// if (code == 0) return; -// String msg = reply.readString(); -// DatabaseUtils.readExceptionFromParcel(reply, msg, code); -// } -// -// public static void readExceptionWithFileNotFoundExceptionFromParcel( -// Parcel reply) throws FileNotFoundException { -// int code = reply.readExceptionCode(); -// if (code == 0) return; -// String msg = reply.readString(); -// if (code == 1) { -// throw new FileNotFoundException(msg); -// } else { -// DatabaseUtils.readExceptionFromParcel(reply, msg, code); -// } -// } -// -// public static void readExceptionWithOperationApplicationExceptionFromParcel( -// Parcel reply) throws OperationApplicationException { -// int code = reply.readExceptionCode(); -// if (code == 0) return; -// String msg = reply.readString(); -// if (code == 10) { -// throw new OperationApplicationException(msg); -// } else { -// DatabaseUtils.readExceptionFromParcel(reply, msg, code); -// } -// } + public static final void readExceptionFromParcel(Parcel reply) { + int code = reply.readExceptionCode(); + if (code == 0) return; + String msg = reply.readString(); + DatabaseUtils.readExceptionFromParcel(reply, msg, code); + } + + public static void readExceptionWithFileNotFoundExceptionFromParcel( + Parcel reply) throws FileNotFoundException { + int code = reply.readExceptionCode(); + if (code == 0) return; + String msg = reply.readString(); + if (code == 1) { + throw new FileNotFoundException(msg); + } else { + DatabaseUtils.readExceptionFromParcel(reply, msg, code); + } + } + + public static void readExceptionWithOperationApplicationExceptionFromParcel( + Parcel reply) throws OperationApplicationException { + int code = reply.readExceptionCode(); + if (code == 0) return; + String msg = reply.readString(); + if (code == 10) { + throw new OperationApplicationException(msg); + } else { + DatabaseUtils.readExceptionFromParcel(reply, msg, code); + } + } private static final void readExceptionFromParcel(Parcel reply, String msg, int code) { switch (code) { case 2: throw new IllegalArgumentException(msg); @@ -1364,15 +1359,11 @@ * of the form returned by sqlite3's .dump command (statements separated by * semicolons) */ static public void createDbFromSqlStatements( Context context, String dbName, int dbVersion, String sqlStatements) { - - File f = context.getDatabasePath(dbName); - f.getParentFile().mkdirs(); - SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f, null); - + SQLiteDatabase db = context.openOrCreateDatabase(dbName, 0, null); // TODO: this is not quite safe since it assumes that all semicolons at the end of a line // terminate statements. It is possible that a text field contains ;\n. We will have to fix // this if that turns out to be a problem. String[] statements = TextUtils.split(sqlStatements, ";\n"); for (String statement : statements) { Index: sqlite3/src/main/java/org/sqlite/database/DefaultDatabaseErrorHandler.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/DefaultDatabaseErrorHandler.java +++ sqlite3/src/main/java/org/sqlite/database/DefaultDatabaseErrorHandler.java @@ -11,41 +11,36 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ - -package org.sqlite.database; +package android.database; import java.io.File; import java.util.List; -import org.sqlite.database.sqlite.SQLiteDatabase; -import org.sqlite.database.sqlite.SQLiteException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; import android.util.Log; import android.util.Pair; /** - * Default class used to define the actions to take when the database corruption is reported + * Default class used to define the action to take when database corruption is reported * by sqlite. *

* An application can specify an implementation of {@link DatabaseErrorHandler} on the * following: *

* The specified {@link DatabaseErrorHandler} is used to handle database corruption errors, if they * occur. *

- * If null is specified for DatabaeErrorHandler param in the above calls, then this class is used + * If null is specified for the DatabaseErrorHandler param in the above calls, this class is used * as the default {@link DatabaseErrorHandler}. */ public final class DefaultDatabaseErrorHandler implements DatabaseErrorHandler { private static final String TAG = "DefaultDatabaseErrorHandler"; @@ -56,14 +51,10 @@ * is detected. */ public void onCorruption(SQLiteDatabase dbObj) { Log.e(TAG, "Corruption reported by sqlite on database: " + dbObj.getPath()); - // If this is a SEE build, do not delete any database files. - // - if( SQLiteDatabase.hasCodec() ) return; - // is the corruption detected even before database could be 'opened'? if (!dbObj.isOpen()) { // database files are not even openable. delete this database file. // NOTE if the database has attached databases, then any of them could be corrupt. // and not deleting all of them could cause corrupted database file to remain and Index: sqlite3/src/main/java/org/sqlite/database/SQLException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/SQLException.java +++ sqlite3/src/main/java/org/sqlite/database/SQLException.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database; +package android.database; /** * An exception that indicates there was an error with SQL parsing or execution. */ public class SQLException extends RuntimeException { Index: sqlite3/src/main/java/org/sqlite/database/sqlite/DatabaseObjectNotClosedException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/DatabaseObjectNotClosedException.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/DatabaseObjectNotClosedException.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; /** * An exception that indicates that garbage-collector is finalizing a database object * that is not explicitly closed * @hide Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteAbortException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteAbortException.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteAbortException.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; /** * An exception that indicates that the SQLite program was aborted. * This can happen either through a call to ABORT in a trigger, * or as the result of using the ABORT conflict clause. Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteAccessPermException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteAccessPermException.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteAccessPermException.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; /** * This exception class is used when sqlite can't access the database file * due to lack of permissions on the file. */ Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; /** * Thrown if the the bind or column parameter index is out of range */ public class SQLiteBindOrColumnIndexOutOfRangeException extends SQLiteException { Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteBlobTooBigException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteBlobTooBigException.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteBlobTooBigException.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; public class SQLiteBlobTooBigException extends SQLiteException { public SQLiteBlobTooBigException() {} public SQLiteBlobTooBigException(String error) { Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteCantOpenDatabaseException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteCantOpenDatabaseException.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteCantOpenDatabaseException.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; public class SQLiteCantOpenDatabaseException extends SQLiteException { public SQLiteCantOpenDatabaseException() {} public SQLiteCantOpenDatabaseException(String error) { Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteClosable.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteClosable.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteClosable.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; import java.io.Closeable; /** * An object created from a SQLiteDatabase that can be closed. Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteConnection.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteConnection.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteConnection.java @@ -11,27 +11,25 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ - -package org.sqlite.database.sqlite; - -/* import dalvik.system.BlockGuard; */ -import org.sqlite.database.sqlite.CloseGuard; + +package android.database.sqlite; + +import dalvik.system.BlockGuard; +import dalvik.system.CloseGuard; import android.database.Cursor; import android.database.CursorWindow; -import org.sqlite.database.DatabaseUtils; -import org.sqlite.database.sqlite.SQLiteDebug.DbStats; +import android.database.DatabaseUtils; +import android.database.sqlite.SQLiteDebug.DbStats; import android.os.CancellationSignal; import android.os.OperationCanceledException; import android.os.ParcelFileDescriptor; +import android.os.SystemClock; +import android.os.Trace; import android.util.Log; import android.util.LruCache; import android.util.Printer; import java.text.SimpleDateFormat; @@ -93,12 +91,10 @@ private static final boolean DEBUG = false; private static final String[] EMPTY_STRING_ARRAY = new String[0]; private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; - private static final Pattern TRIM_SQL_PATTERN = Pattern.compile("[\\s]*\\n+[\\s]*"); - private final CloseGuard mCloseGuard = CloseGuard.get(); private final SQLiteConnectionPool mPool; private final SQLiteDatabaseConfiguration mConfiguration; private final int mConnectionId; @@ -153,19 +149,16 @@ long connectionPtr, long statementPtr); private static native int nativeExecuteForChangedRowCount(long connectionPtr, long statementPtr); private static native long nativeExecuteForLastInsertedRowId( long connectionPtr, long statementPtr); private static native long nativeExecuteForCursorWindow( - long connectionPtr, long statementPtr, CursorWindow win, + long connectionPtr, long statementPtr, long windowPtr, int startPos, int requiredPos, boolean countAllRows); private static native int nativeGetDbLookaside(long connectionPtr); private static native void nativeCancel(long connectionPtr); private static native void nativeResetCancel(long connectionPtr, boolean cancelable); - private static native boolean nativeHasCodec(); - public static boolean hasCodec(){ return nativeHasCodec(); } - private SQLiteConnection(SQLiteConnectionPool pool, SQLiteDatabaseConfiguration configuration, int connectionId, boolean primaryConnection) { mPool = pool; mConfiguration = new SQLiteDatabaseConfiguration(configuration); @@ -217,13 +210,13 @@ mConfiguration.label, SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME); setPageSize(); setForeignKeyModeFromConfiguration(); + setWalModeFromConfiguration(); setJournalSizeLimit(); - setAutoCheckpointInterval(); - setWalModeFromConfiguration(); + setAutoCheckpointInterval(); setLocaleFromConfiguration(); // Register custom functions. final int functionCount = mConfiguration.customFunctions.size(); for (int i = 0; i < functionCount; i++) { @@ -400,16 +393,10 @@ throw new SQLiteException("Failed to change locale for db '" + mConfiguration.label + "' to '" + newLocale + "'.", ex); } } - public void enableLocalizedCollators(){ - if( nativeHasCodec() ){ - setLocaleFromConfiguration(); - } - } - // Called by SQLiteConnectionPool only. void reconfigure(SQLiteDatabaseConfiguration configuration) { mOnlyAllowReadOnlyOperations = false; // Register custom functions. @@ -430,11 +417,11 @@ // Update configuration parameters. mConfiguration.updateParametersFrom(configuration); // Update prepared statement cache size. - /* mPreparedStatementCache.resize(configuration.maxSqlCacheSize); */ + mPreparedStatementCache.resize(configuration.maxSqlCacheSize); // Update foreign key mode. if (foreignKeyModeChanged) { setForeignKeyModeFromConfiguration(); } @@ -854,11 +841,11 @@ bindArguments(statement, bindArgs); applyBlockGuardPolicy(statement); attachCancellationSignal(cancellationSignal); try { final long result = nativeExecuteForCursorWindow( - mConnectionPtr, statement.mStatementPtr, window, + mConnectionPtr, statement.mStatementPtr, window.mWindowPtr, startPos, requiredPos, countAllRows); actualPos = (int)(result >> 32); countedRows = (int)result; filledRows = window.getNumRows(); window.setStartPosition(actualPos); @@ -1047,17 +1034,17 @@ } return false; } private void applyBlockGuardPolicy(PreparedStatement statement) { -/* if (!mConfiguration.isInMemoryDb()) {*/ -/* if (statement.mReadOnly) {*/ -/* BlockGuard.getThreadPolicy().onReadFromDisk();*/ -/* } else {*/ -/* BlockGuard.getThreadPolicy().onWriteToDisk();*/ -/* }*/ -/* }*/ + if (!mConfiguration.isInMemoryDb()) { + if (statement.mReadOnly) { + BlockGuard.getThreadPolicy().onReadFromDisk(); + } else { + BlockGuard.getThreadPolicy().onWriteToDisk(); + } + } } /** * Dumps debugging information about this connection. * @@ -1214,11 +1201,15 @@ statement.mPoolNext = mPreparedStatementPool; mPreparedStatementPool = statement; } private static String trimSqlForDisplay(String sql) { - return TRIM_SQL_PATTERN.matcher(sql).replaceAll(" "); + // Note: Creating and caching a regular expression is expensive at preload-time + // and stops compile-time initialization. This pattern is only used when + // dumping the connection, which is a rare (mainly error) case. So: + // DO NOT CACHE. + return sql.replaceAll("[\\s]*\\n+[\\s]*", " "); } /** * Holder type for a prepared statement. * @@ -1319,11 +1310,12 @@ operation.mException = null; if (operation.mBindArgs != null) { operation.mBindArgs.clear(); } } - operation.mStartTime = System.currentTimeMillis(); + operation.mStartWallTime = System.currentTimeMillis(); + operation.mStartTime = SystemClock.uptimeMillis(); operation.mKind = kind; operation.mSql = sql; if (bindArgs != null) { if (operation.mBindArgs == null) { operation.mBindArgs = new ArrayList(); @@ -1339,10 +1331,14 @@ operation.mBindArgs.add(arg); } } } operation.mCookie = newOperationCookieLocked(index); + if (Trace.isTagEnabled(Trace.TRACE_TAG_DATABASE)) { + Trace.asyncTraceBegin(Trace.TRACE_TAG_DATABASE, operation.getTraceMethodName(), + operation.mCookie); + } mIndex = index; return operation.mCookie; } } @@ -1376,11 +1372,15 @@ } private boolean endOperationDeferLogLocked(int cookie) { final Operation operation = getOperationLocked(cookie); if (operation != null) { - operation.mEndTime = System.currentTimeMillis(); + if (Trace.isTagEnabled(Trace.TRACE_TAG_DATABASE)) { + Trace.asyncTraceEnd(Trace.TRACE_TAG_DATABASE, operation.getTraceMethodName(), + operation.mCookie); + } + operation.mEndTime = SystemClock.uptimeMillis(); operation.mFinished = true; return SQLiteDebug.DEBUG_LOG_SLOW_QUERIES && SQLiteDebug.shouldLogSlowQuery( operation.mEndTime - operation.mStartTime); } return false; @@ -1448,15 +1448,19 @@ } } } private static final class Operation { - private static final SimpleDateFormat sDateFormat = - new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + // Trim all SQL statements to 256 characters inside the trace marker. + // This limit gives plenty of context while leaving space for other + // entries in the trace buffer (and ensures atrace doesn't truncate the + // marker for us, potentially losing metadata in the process). + private static final int MAX_TRACE_METHOD_NAME_LEN = 256; - public long mStartTime; - public long mEndTime; + public long mStartWallTime; // in System.currentTimeMillis() + public long mStartTime; // in SystemClock.uptimeMillis(); + public long mEndTime; // in SystemClock.uptimeMillis(); public String mKind; public String mSql; public ArrayList mBindArgs; public boolean mFinished; public Exception mException; @@ -1465,11 +1469,11 @@ public void describe(StringBuilder msg, boolean verbose) { msg.append(mKind); if (mFinished) { msg.append(" took ").append(mEndTime - mStartTime).append("ms"); } else { - msg.append(" started ").append(System.currentTimeMillis() - mStartTime) + msg.append(" started ").append(System.currentTimeMillis() - mStartWallTime) .append("ms ago"); } msg.append(" - ").append(getStatus()); if (mSql != null) { msg.append(", sql=\"").append(trimSqlForDisplay(mSql)).append("\""); @@ -1503,11 +1507,22 @@ if (!mFinished) { return "running"; } return mException != null ? "failed" : "succeeded"; } + + private String getTraceMethodName() { + String methodName = mKind + " " + mSql; + if (methodName.length() > MAX_TRACE_METHOD_NAME_LEN) + return methodName.substring(0, MAX_TRACE_METHOD_NAME_LEN); + return methodName; + } private String getFormattedStartTime() { - return sDateFormat.format(new Date(mStartTime)); + // Note: SimpleDateFormat is not thread-safe, cannot be compile-time created, and is + // relatively expensive to create during preloading. This method is only used + // when dumping a connection, which is a rare (mainly error) case. So: + // DO NOT CACHE. + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(mStartWallTime)); } } } Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteConnectionPool.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteConnectionPool.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteConnectionPool.java @@ -11,25 +11,21 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ - -package org.sqlite.database.sqlite; - -import org.sqlite.database.sqlite.CloseGuard; - -import org.sqlite.database.sqlite.SQLiteDebug.DbStats; + +package android.database.sqlite; + +import dalvik.system.CloseGuard; + +import android.database.sqlite.SQLiteDebug.DbStats; import android.os.CancellationSignal; import android.os.OperationCanceledException; import android.os.SystemClock; import android.util.Log; -/* import android.util.PrefixPrinter; */ +import android.util.PrefixPrinter; import android.util.Printer; import java.io.Closeable; import java.util.ArrayList; import java.util.Map; @@ -994,30 +990,18 @@ waiter.mException = null; waiter.mNonce += 1; mConnectionWaiterPool = waiter; } - public void enableLocalizedCollators() { - synchronized (mLock) { - if( !mAcquiredConnections.isEmpty() || mAvailablePrimaryConnection==null ) { - throw new IllegalStateException( - "Cannot enable localized collators while database is in use" - ); - } - mAvailablePrimaryConnection.enableLocalizedCollators(); - } - } - /** * Dumps debugging information about this connection pool. * * @param printer The printer to receive the dump, not null. * @param verbose True to dump more verbose information. */ public void dump(Printer printer, boolean verbose) { - /* - Printer indentedPrinter = Printer.create(printer, " "); + Printer indentedPrinter = PrefixPrinter.create(printer, " "); synchronized (mLock) { printer.println("Connection pool for " + mConfiguration.path + ":"); printer.println(" Open: " + mIsOpen); printer.println(" Max connections: " + mMaxConnectionPoolSize); @@ -1064,11 +1048,10 @@ } } else { indentedPrinter.println(""); } } - */ } @Override public String toString() { return "SQLiteConnectionPool: " + mConfiguration.path; Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteConstraintException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteConstraintException.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteConstraintException.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; /** * An exception that indicates that an integrity constraint was violated. */ public class SQLiteConstraintException extends SQLiteException { Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteCursor.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteCursor.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteCursor.java @@ -11,22 +11,16 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ - -package org.sqlite.database.sqlite; - -import org.sqlite.database.DatabaseUtils; + +package android.database.sqlite; import android.database.AbstractWindowedCursor; import android.database.CursorWindow; - +import android.database.DatabaseUtils; import android.os.StrictMode; import android.util.Log; import java.util.HashMap; import java.util.Map; @@ -98,11 +92,11 @@ */ public SQLiteCursor(SQLiteCursorDriver driver, String editTable, SQLiteQuery query) { if (query == null) { throw new IllegalArgumentException("query object cannot be null"); } - if (/* StrictMode.vmSqliteObjectLeaksEnabled() */ false ) { + if (StrictMode.vmSqliteObjectLeaksEnabled()) { mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace(); } else { mStackTrace = null; } mDriver = driver; @@ -138,31 +132,12 @@ fillWindow(0); } return mCount; } - /* - ** The AbstractWindowClass contains protected methods clearOrCreateWindow() and - ** closeWindow(), which are used by the android.database.sqlite.* version of this - ** class. But, since they are marked with "@hide", the following replacement - ** versions are required. - */ - private void awc_clearOrCreateWindow(String name){ - CursorWindow win = getWindow(); - if( win==null ){ - win = new CursorWindow(name); - setWindow(win); - }else{ - win.clear(); - } - } - private void awc_closeWindow(){ - setWindow(null); - } - private void fillWindow(int requiredPos) { - awc_clearOrCreateWindow(getDatabase().getPath()); + clearOrCreateWindow(getDatabase().getPath()); try { if (mCount == NO_COUNT) { int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, 0); mCount = mQuery.fillWindow(mWindow, startPos, requiredPos, true); @@ -178,11 +153,11 @@ } catch (RuntimeException ex) { // Close the cursor window if the query failed and therefore will // not produce any results. This helps to avoid accidentally leaking // the cursor window if the client does not correctly handle exceptions // and fails to close the cursor. - awc_closeWindow(); + closeWindow(); throw ex; } } @Override @@ -282,11 +257,10 @@ @Override protected void finalize() { try { // if the cursor hasn't been closed yet, close it first if (mWindow != null) { - /* if (mStackTrace != null) { String sql = mQuery.getSql(); int len = sql.length(); StrictMode.onSqliteObjectLeaked( "Finalizing a Cursor that has not been deactivated or closed. " + @@ -293,13 +267,12 @@ "database = " + mQuery.getDatabase().getLabel() + ", table = " + mEditTable + ", query = " + sql.substring(0, (len > 1000) ? 1000 : len), mStackTrace); } - */ close(); } } finally { super.finalize(); } } } Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteCursorDriver.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteCursorDriver.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteCursorDriver.java @@ -11,19 +11,15 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; import android.database.Cursor; -import org.sqlite.database.sqlite.SQLiteDatabase.CursorFactory; +import android.database.sqlite.SQLiteDatabase.CursorFactory; /** * A driver for SQLiteCursors that is used to create them and gets notified * by the cursors it creates on significant events in their lifetimes. */ Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteCustomFunction.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteCustomFunction.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteCustomFunction.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; /** * Describes a custom SQL function. * * @hide Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDatabase.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDatabase.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDatabase.java @@ -11,34 +11,32 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.ContentValues; import android.database.Cursor; -import org.sqlite.database.DatabaseErrorHandler; -import org.sqlite.database.DatabaseUtils; -import org.sqlite.database.DefaultDatabaseErrorHandler; -import org.sqlite.database.SQLException; -import org.sqlite.database.sqlite.SQLiteDebug.DbStats; +import android.database.DatabaseErrorHandler; +import android.database.DatabaseUtils; +import android.database.DefaultDatabaseErrorHandler; +import android.database.SQLException; +import android.database.sqlite.SQLiteDebug.DbStats; import android.os.CancellationSignal; import android.os.Looper; import android.os.OperationCanceledException; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; import android.util.Pair; import android.util.Printer; -import org.sqlite.database.sqlite.CloseGuard; +import dalvik.system.CloseGuard; import java.io.File; import java.io.FileFilter; import java.util.ArrayList; import java.util.HashMap; @@ -740,18 +738,20 @@ deleted |= new File(file.getPath() + "-wal").delete(); File dir = file.getParentFile(); if (dir != null) { final String prefix = file.getName() + "-mj"; - final FileFilter filter = new FileFilter() { + File[] files = dir.listFiles(new FileFilter() { @Override public boolean accept(File candidate) { return candidate.getName().startsWith(prefix); } - }; - for (File masterJournal : dir.listFiles(filter)) { - deleted |= masterJournal.delete(); + }); + if (files != null) { + for (File masterJournal : files) { + deleted |= masterJournal.delete(); + } } } return deleted; } @@ -1369,10 +1369,11 @@ return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE); } /** * Convenience method for replacing a row in the database. + * Inserts a new row if a row does not already exist. * * @param table the table in which to replace the row * @param nullColumnHack optional; may be null. * SQL doesn't allow inserting a completely empty row without * naming at least one column name. If your provided initialValues is @@ -1379,11 +1380,11 @@ * empty, no column names are known and an empty row can't be inserted. * If not set to null, the nullColumnHack parameter * provides the name of nullable column name to explicitly insert a NULL into * in the case where your initialValues is empty. * @param initialValues this map contains the initial column values for - * the row. + * the row. The keys should be the column names and the values the column values. * @return the row ID of the newly inserted row, or -1 if an error occurred */ public long replace(String table, String nullColumnHack, ContentValues initialValues) { try { return insertWithOnConflict(table, nullColumnHack, initialValues, @@ -1394,10 +1395,11 @@ } } /** * Convenience method for replacing a row in the database. + * Inserts a new row if a row does not already exist. * * @param table the table in which to replace the row * @param nullColumnHack optional; may be null. * SQL doesn't allow inserting a completely empty row without * naming at least one column name. If your provided initialValues is @@ -1404,11 +1406,11 @@ * empty, no column names are known and an empty row can't be inserted. * If not set to null, the nullColumnHack parameter * provides the name of nullable column name to explicitly insert a NULL into * in the case where your initialValues is empty. * @param initialValues this map contains the initial column values for - * the row. The key + * the row. The keys should be the column names and the values the column values. * @throws SQLException * @return the row ID of the newly inserted row, or -1 if an error occurred */ public long replaceOrThrow(String table, String nullColumnHack, ContentValues initialValues) throws SQLException { @@ -1429,14 +1431,13 @@ * in the case where your initialValues is empty. * @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 conflictAlgorithm for insert conflict resolver - * @return the row ID of the newly inserted row - * OR the primary key of the existing row if the input param 'conflictAlgorithm' = - * {@link #CONFLICT_IGNORE} - * OR -1 if any error + * @return the row ID of the newly inserted row OR -1 if either the + * input parameter conflictAlgorithm = {@link #CONFLICT_IGNORE} + * or an error occurred. */ public long insertWithOnConflict(String table, String nullColumnHack, ContentValues initialValues, int conflictAlgorithm) { acquireReference(); try { @@ -1446,11 +1447,11 @@ sql.append(" INTO "); sql.append(table); sql.append('('); Object[] bindArgs = null; - int size = (initialValues != null && initialValues.size() > 0) + int size = (initialValues != null && !initialValues.isEmpty()) ? initialValues.size() : 0; if (size > 0) { bindArgs = new Object[size]; int i = 0; for (String colName : initialValues.keySet()) { @@ -1538,11 +1539,11 @@ * @param conflictAlgorithm for update conflict resolver * @return the number of rows affected */ public int updateWithOnConflict(String table, ContentValues values, String whereClause, String[] whereArgs, int conflictAlgorithm) { - if (values == null || values.size() == 0) { + if (values == null || values.isEmpty()) { throw new IllegalArgumentException("Empty values"); } acquireReference(); try { @@ -1681,10 +1682,25 @@ } } finally { releaseReference(); } } + + /** + * Verifies that a SQL SELECT statement is valid by compiling it. + * If the SQL statement is not valid, this method will throw a {@link SQLiteException}. + * + * @param sql SQL to be validated + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. + * If the operation is canceled, then {@link OperationCanceledException} will be thrown + * when the query is executed. + * @throws SQLiteException if {@code sql} is invalid + */ + public void validateSql(@NonNull String sql, @Nullable CancellationSignal cancellationSignal) { + getThreadSession().prepare(sql, + getThreadDefaultConnectionFlags(/* readOnly =*/ true), cancellationSignal, null); + } /** * Returns true if the database is opened as read only. * * @return True if database is opened as read only. @@ -1724,11 +1740,11 @@ /** * Returns true if the new version code is greater than the current database version. * * @param newVersion The new version code. - * @return True if the new version code is greater than the current database version. + * @return True if the new version code is greater than the current database version. */ public boolean needUpgrade(int newVersion) { return newVersion > getVersion(); } @@ -2192,14 +2208,6 @@ * @hide */ public interface CustomFunction { public void callback(String[] args); } - - public static boolean hasCodec() { - return SQLiteConnection.hasCodec(); - } - - public void enableLocalizedCollators() { - mConnectionPoolLocked.enableLocalizedCollators(); - } } Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDatabaseConfiguration.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDatabaseConfiguration.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDatabaseConfiguration.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; import java.util.ArrayList; import java.util.Locale; import java.util.regex.Pattern; @@ -159,17 +155,11 @@ public boolean isInMemoryDb() { return path.equalsIgnoreCase(MEMORY_DB_PATH); } private static String stripPathForLogs(String path) { - /* Strip off all URI parameters. */ - int iIdx = path.indexOf('?'); - if( iIdx>=0 ){ - path = (String) path.subSequence(0, iIdx); - } - if (path.indexOf('@') == -1) { return path; } return EMAIL_IN_DB_PATTERN.matcher(path).replaceAll("XX@YY"); } } Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDatabaseCorruptException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDatabaseCorruptException.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDatabaseCorruptException.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; /** * An exception that indicates that the SQLite database file is corrupt. */ public class SQLiteDatabaseCorruptException extends SQLiteException { Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDatabaseLockedException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDatabaseLockedException.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDatabaseLockedException.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; /** * Thrown if the database engine was unable to acquire the * database locks it needs to do its job. If the statement is a [COMMIT] * or occurs outside of an explicit transaction, then you can retry the Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDatatypeMismatchException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDatatypeMismatchException.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDatatypeMismatchException.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; public class SQLiteDatatypeMismatchException extends SQLiteException { public SQLiteDatatypeMismatchException() {} public SQLiteDatatypeMismatchException(String error) { Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDebug.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDebug.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDebug.java @@ -11,21 +11,17 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; import java.util.ArrayList; import android.os.Build; -/* import android.os.SystemProperties; */ +import android.os.SystemProperties; import android.util.Log; import android.util.Printer; /** * Provides debugging info about all SQLite databases running in the current process. @@ -62,11 +58,11 @@ /** * True to enable database performance testing instrumentation. * @hide */ - public static final boolean DEBUG_LOG_SLOW_QUERIES = false; + public static final boolean DEBUG_LOG_SLOW_QUERIES = Build.IS_DEBUGGABLE; private SQLiteDebug() { } /** @@ -81,11 +77,11 @@ * For example, "adb shell setprop db.log.slow_query_threshold 200" will * log all queries that take 200ms or longer to run. * @hide */ public static final boolean shouldLogSlowQuery(long elapsedTimeMillis) { - int slowQueryMillis = 10000; + int slowQueryMillis = SystemProperties.getInt("db.log.slow_query_threshold", -1); return slowQueryMillis >= 0 && elapsedTimeMillis >= slowQueryMillis; } /** * Contains statistics about the active pagers in the current process. Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDirectCursorDriver.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDirectCursorDriver.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDirectCursorDriver.java @@ -11,19 +11,15 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; import android.database.Cursor; -import org.sqlite.database.sqlite.SQLiteDatabase.CursorFactory; +import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.os.CancellationSignal; /** * A cursor driver that uses the given query directly. * Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDiskIOException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDiskIOException.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDiskIOException.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; /** * An exception that indicates that an IO error occured while accessing the * SQLite database file. */ Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDoneException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDoneException.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteDoneException.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; /** * An exception that indicates that the SQLite program is done. * Thrown when an operation that expects a row (such as {@link * SQLiteStatement#simpleQueryForString} or {@link Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteException.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteException.java @@ -11,18 +11,14 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ - -package org.sqlite.database.sqlite; - -import org.sqlite.database.SQLException; + +package android.database.sqlite; + +import android.database.SQLException; /** * A SQLite exception that indicates there was an error with SQL parsing or execution. */ public class SQLiteException extends SQLException { Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteFullException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteFullException.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteFullException.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; /** * An exception that indicates that the SQLite database is full. */ public class SQLiteFullException extends SQLiteException { Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteGlobal.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteGlobal.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteGlobal.java @@ -11,20 +11,16 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; import android.content.res.Resources; import android.os.StatFs; -/* import android.os.SystemProperties; */ +import android.os.SystemProperties; /** * Provides access to SQLite functions that affect all database connection, * such as memory management. * @@ -63,55 +59,69 @@ * Gets the default page size to use when creating a database. */ public static int getDefaultPageSize() { synchronized (sLock) { if (sDefaultPageSize == 0) { + // If there is an issue accessing /data, something is so seriously + // wrong that we just let the IllegalArgumentException propagate. sDefaultPageSize = new StatFs("/data").getBlockSize(); } - return 1024; + return SystemProperties.getInt("debug.sqlite.pagesize", sDefaultPageSize); } } /** * Gets the default journal mode when WAL is not in use. */ public static String getDefaultJournalMode() { - return "delete"; + return SystemProperties.get("debug.sqlite.journalmode", + Resources.getSystem().getString( + com.android.internal.R.string.db_default_journal_mode)); } /** * Gets the journal size limit in bytes. */ public static int getJournalSizeLimit() { - return 10000; + return SystemProperties.getInt("debug.sqlite.journalsizelimit", + Resources.getSystem().getInteger( + com.android.internal.R.integer.db_journal_size_limit)); } /** * Gets the default database synchronization mode when WAL is not in use. */ public static String getDefaultSyncMode() { - return "normal"; + return SystemProperties.get("debug.sqlite.syncmode", + Resources.getSystem().getString( + com.android.internal.R.string.db_default_sync_mode)); } /** * Gets the database synchronization mode when in WAL mode. */ public static String getWALSyncMode() { - return "normal"; + return SystemProperties.get("debug.sqlite.wal.syncmode", + Resources.getSystem().getString( + com.android.internal.R.string.db_wal_sync_mode)); } /** * Gets the WAL auto-checkpoint integer in database pages. */ public static int getWALAutoCheckpoint() { - int value = 1000; + int value = SystemProperties.getInt("debug.sqlite.wal.autocheckpoint", + Resources.getSystem().getInteger( + com.android.internal.R.integer.db_wal_autocheckpoint)); return Math.max(1, value); } /** * Gets the connection pool size when in WAL mode. */ public static int getWALConnectionPoolSize() { - int value = 10; + int value = SystemProperties.getInt("debug.sqlite.wal.poolsize", + Resources.getSystem().getInteger( + com.android.internal.R.integer.db_connection_pool_size)); return Math.max(2, value); } } Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteMisuseException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteMisuseException.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteMisuseException.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; /** * This error can occur if the application creates a SQLiteStatement object and allows multiple * threads in the application use it at the same time. * Sqlite returns this error if bind and execute methods on this object occur at the same time Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteOpenHelper.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteOpenHelper.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteOpenHelper.java @@ -11,22 +11,18 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; import android.content.Context; -import org.sqlite.database.DatabaseErrorHandler; -import org.sqlite.database.DefaultDatabaseErrorHandler; -import org.sqlite.database.sqlite.SQLiteDatabase.CursorFactory; +import android.database.DatabaseErrorHandler; +import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.util.Log; +import java.io.File; /** * A helper class to manage database creation and version management. * *

You create a subclass implementing {@link #onCreate}, {@link #onUpgrade} and @@ -57,10 +53,11 @@ private final Context mContext; private final String mName; private final CursorFactory mFactory; private final int mNewVersion; + private final int mMinimumSupportedVersion; private SQLiteDatabase mDatabase; private boolean mIsInitializing; private boolean mEnableWriteAheadLogging; private final DatabaseErrorHandler mErrorHandler; @@ -99,17 +96,46 @@ * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database * corruption, or null to use the default error handler. */ public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version, DatabaseErrorHandler errorHandler) { + this(context, name, factory, version, 0, errorHandler); + } + + /** + * Same as {@link #SQLiteOpenHelper(Context, String, CursorFactory, int, DatabaseErrorHandler)} + * but also accepts an integer minimumSupportedVersion as a convenience for upgrading very old + * versions of this database that are no longer supported. If a database with older version that + * minimumSupportedVersion is found, it is simply deleted and a new database is created with the + * given name and version + * + * @param context to use to open or create the database + * @param name the name of the database file, null for a temporary in-memory database + * @param factory to use for creating cursor objects, null for default + * @param version the required version of the database + * @param minimumSupportedVersion the minimum version that is supported to be upgraded to + * {@code version} via {@link #onUpgrade}. If the current database version is lower + * than this, database is simply deleted and recreated with the version passed in + * {@code version}. {@link #onBeforeDelete} is called before deleting the database + * when this happens. This is 0 by default. + * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database + * corruption, or null to use the default error handler. + * @see #onBeforeDelete(SQLiteDatabase) + * @see #SQLiteOpenHelper(Context, String, CursorFactory, int, DatabaseErrorHandler) + * @see #onUpgrade(SQLiteDatabase, int, int) + * @hide + */ + public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version, + int minimumSupportedVersion, DatabaseErrorHandler errorHandler) { if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version); mContext = context; mName = name; mFactory = factory; mNewVersion = version; mErrorHandler = errorHandler; + mMinimumSupportedVersion = Math.max(0, minimumSupportedVersion); } /** * Return the name of the SQLite database being opened, as given to * the constructor. @@ -223,13 +249,13 @@ if (DEBUG_STRICT_READONLY && !writable) { final String path = mContext.getDatabasePath(mName).getPath(); db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY, mErrorHandler); } else { - db = SQLiteDatabase.openOrCreateDatabase( - mName, mFactory, mErrorHandler - ); + db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ? + Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0, + mFactory, mErrorHandler); } } catch (SQLiteException ex) { if (writable) { throw ex; } @@ -248,25 +274,38 @@ if (db.isReadOnly()) { throw new SQLiteException("Can't upgrade read-only database from version " + db.getVersion() + " to " + mNewVersion + ": " + mName); } - db.beginTransaction(); - try { - if (version == 0) { - onCreate(db); - } else { - if (version > mNewVersion) { - onDowngrade(db, version, mNewVersion); - } else { - onUpgrade(db, version, mNewVersion); - } - } - db.setVersion(mNewVersion); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); + if (version > 0 && version < mMinimumSupportedVersion) { + File databaseFile = new File(db.getPath()); + onBeforeDelete(db); + db.close(); + if (SQLiteDatabase.deleteDatabase(databaseFile)) { + mIsInitializing = false; + return getDatabaseLocked(writable); + } else { + throw new IllegalStateException("Unable to delete obsolete database " + + mName + " with version " + version); + } + } else { + db.beginTransaction(); + try { + if (version == 0) { + onCreate(db); + } else { + if (version > mNewVersion) { + onDowngrade(db, version, mNewVersion); + } else { + onUpgrade(db, version, mNewVersion); + } + } + db.setVersion(mNewVersion); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } } } onOpen(db); @@ -295,27 +334,41 @@ mDatabase = null; } } /** - * Called when the database connection is being configured, to enable features - * such as write-ahead logging or foreign key support. + * Called when the database connection is being configured, to enable features such as + * write-ahead logging or foreign key support. + *

+ * This method is called before {@link #onCreate}, {@link #onUpgrade}, {@link #onDowngrade}, or + * {@link #onOpen} are called. It should not modify the database except to configure the + * database connection as required. + *

*

- * This method is called before {@link #onCreate}, {@link #onUpgrade}, - * {@link #onDowngrade}, or {@link #onOpen} are called. It should not modify - * the database except to configure the database connection as required. - *

- * This method should only call methods that configure the parameters of the - * database connection, such as {@link SQLiteDatabase#enableWriteAheadLogging} - * {@link SQLiteDatabase#setForeignKeyConstraintsEnabled}, - * {@link SQLiteDatabase#setLocale}, {@link SQLiteDatabase#setMaximumSize}, - * or executing PRAGMA statements. + * This method should only call methods that configure the parameters of the database + * connection, such as {@link SQLiteDatabase#enableWriteAheadLogging} + * {@link SQLiteDatabase#setForeignKeyConstraintsEnabled}, {@link SQLiteDatabase#setLocale}, + * {@link SQLiteDatabase#setMaximumSize}, or executing PRAGMA statements. *

* * @param db The database. */ public void onConfigure(SQLiteDatabase db) {} + + /** + * Called before the database is deleted when the version returned by + * {@link SQLiteDatabase#getVersion()} is lower than the minimum supported version passed (if at + * all) while creating this helper. After the database is deleted, a fresh database with the + * given version is created. This will be followed by {@link #onConfigure(SQLiteDatabase)} and + * {@link #onCreate(SQLiteDatabase)} being called with a new SQLiteDatabase object + * + * @param db the database opened with this helper + * @see #SQLiteOpenHelper(Context, String, CursorFactory, int, int, DatabaseErrorHandler) + * @hide + */ + public void onBeforeDelete(SQLiteDatabase db) { + } /** * Called when the database is created for the first time. This is where the * creation of tables and the initial population of the tables should happen. * Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteOutOfMemoryException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteOutOfMemoryException.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteOutOfMemoryException.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; public class SQLiteOutOfMemoryException extends SQLiteException { public SQLiteOutOfMemoryException() {} public SQLiteOutOfMemoryException(String error) { Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteProgram.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteProgram.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteProgram.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; import android.database.DatabaseUtils; import android.os.CancellationSignal; import java.util.Arrays; Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteQuery.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteQuery.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteQuery.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; import android.database.CursorWindow; import android.os.CancellationSignal; import android.os.OperationCanceledException; import android.util.Log; Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteQueryBuilder.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteQueryBuilder.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteQueryBuilder.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; import android.database.Cursor; import android.database.DatabaseUtils; import android.os.CancellationSignal; import android.os.OperationCanceledException; @@ -388,12 +384,11 @@ // originally specified. An attacker cannot create an expression that // would escape the SQL expression while maintaining balanced parentheses // in both the wrapped and original forms. String sqlForValidation = buildQuery(projectionIn, "(" + selection + ")", groupBy, having, sortOrder, limit); - validateQuerySql(db, sqlForValidation, - cancellationSignal); // will throw if query is invalid + db.validateSql(sqlForValidation, cancellationSignal); // will throw if query is invalid } String sql = buildQuery( projectionIn, selection, groupBy, having, sortOrder, limit); @@ -405,20 +400,10 @@ mFactory, sql, selectionArgs, SQLiteDatabase.findEditTable(mTables), cancellationSignal); // will throw if query is invalid } - /** - * Verifies that a SQL SELECT statement is valid by compiling it. - * If the SQL statement is not valid, this method will throw a {@link SQLiteException}. - */ - private void validateQuerySql(SQLiteDatabase db, String sql, - CancellationSignal cancellationSignal) { - db.getThreadSession().prepare(sql, - db.getThreadDefaultConnectionFlags(true /*readOnly*/), cancellationSignal, null); - } - /** * Construct a SELECT statement suitable for use in a group of * SELECT statements that will be joined through UNION operators * in buildUnionQuery. * Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteReadOnlyDatabaseException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteReadOnlyDatabaseException.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteReadOnlyDatabaseException.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; public class SQLiteReadOnlyDatabaseException extends SQLiteException { public SQLiteReadOnlyDatabaseException() {} public SQLiteReadOnlyDatabaseException(String error) { Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteSession.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteSession.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteSession.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; import android.database.CursorWindow; import android.database.DatabaseUtils; import android.os.CancellationSignal; import android.os.OperationCanceledException; Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteStatement.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteStatement.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteStatement.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; import android.os.ParcelFileDescriptor; /** * Represents a statement that can be executed against a database. The statement @@ -37,11 +33,11 @@ /** * Execute this SQL statement, if it is not a SELECT / INSERT / DELETE / UPDATE, for example * CREATE / DROP table, view, trigger, index etc. * - * @throws org.sqlite.database.SQLException If the SQL string is invalid for + * @throws android.database.SQLException If the SQL string is invalid for * some reason */ public void execute() { acquireReference(); try { @@ -57,11 +53,11 @@ /** * Execute this SQL statement, if the the number of rows affected by execution of this SQL * statement is of any importance to the caller - for example, UPDATE / DELETE SQL statements. * * @return the number of rows affected by this SQL statement execution. - * @throws org.sqlite.database.SQLException If the SQL string is invalid for + * @throws android.database.SQLException If the SQL string is invalid for * some reason */ public int executeUpdateDelete() { acquireReference(); try { @@ -79,11 +75,11 @@ * Execute this SQL statement and return the ID of the row inserted due to this call. * The SQL statement should be an INSERT for this to be a useful call. * * @return the row ID of the last row inserted, if this insert is successful. -1 otherwise. * - * @throws org.sqlite.database.SQLException If the SQL string is invalid for + * @throws android.database.SQLException If the SQL string is invalid for * some reason */ public long executeInsert() { acquireReference(); try { @@ -101,11 +97,11 @@ * Execute a statement that returns a 1 by 1 table with a numeric value. * For example, SELECT COUNT(*) FROM table; * * @return The result of the query. * - * @throws org.sqlite.database.sqlite.SQLiteDoneException if the query returns zero rows + * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows */ public long simpleQueryForLong() { acquireReference(); try { return getSession().executeForLong( @@ -122,11 +118,11 @@ * Execute a statement that returns a 1 by 1 table with a text value. * For example, SELECT COUNT(*) FROM table; * * @return The result of the query. * - * @throws org.sqlite.database.sqlite.SQLiteDoneException if the query returns zero rows + * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows */ public String simpleQueryForString() { acquireReference(); try { return getSession().executeForString( @@ -143,11 +139,11 @@ * Executes a statement that returns a 1 by 1 table with a blob value. * * @return A read-only file descriptor for a copy of the blob value, or {@code null} * if the value is null or could not be read for some reason. * - * @throws org.sqlite.database.sqlite.SQLiteDoneException if the query returns zero rows + * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows */ public ParcelFileDescriptor simpleQueryForBlobFileDescriptor() { acquireReference(); try { return getSession().executeForBlobFileDescriptor( Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteStatementInfo.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteStatementInfo.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteStatementInfo.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; /** * Describes a SQLite statement. * * @hide Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteTableLockedException.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteTableLockedException.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteTableLockedException.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; public class SQLiteTableLockedException extends SQLiteException { public SQLiteTableLockedException() {} public SQLiteTableLockedException(String error) { Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteTransactionListener.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteTransactionListener.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SQLiteTransactionListener.java @@ -11,16 +11,12 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; /** * A listener for transaction events. */ public interface SQLiteTransactionListener { Index: sqlite3/src/main/java/org/sqlite/database/sqlite/SqliteWrapper.java ================================================================== --- sqlite3/src/main/java/org/sqlite/database/sqlite/SqliteWrapper.java +++ sqlite3/src/main/java/org/sqlite/database/sqlite/SqliteWrapper.java @@ -12,22 +12,18 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ -package org.sqlite.database.sqlite; +package android.database.sqlite; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; -import org.sqlite.database.sqlite.SQLiteException; +import android.database.sqlite.SQLiteException; import android.net.Uri; import android.util.Log; import android.widget.Toast; /** @@ -48,11 +44,12 @@ return e.getMessage().equals(SQLITE_EXCEPTION_DETAIL_MESSAGE); } public static void checkSQLiteException(Context context, SQLiteException e) { if (isLowMemory(e)) { - Toast.makeText(context, "low memory", Toast.LENGTH_SHORT).show(); + Toast.makeText(context, com.android.internal.R.string.low_memory, + Toast.LENGTH_SHORT).show(); } else { throw e; } } Index: sqlite3/src/main/jni/sqlite/android_database_SQLiteCommon.cpp ================================================================== --- sqlite3/src/main/jni/sqlite/android_database_SQLiteCommon.cpp +++ sqlite3/src/main/jni/sqlite/android_database_SQLiteCommon.cpp @@ -11,16 +11,14 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ #include "android_database_SQLiteCommon.h" + +#include namespace android { /* throw a SQLiteException with a message appropriate for the error in handle */ void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) { @@ -63,76 +61,78 @@ void throw_sqlite3_exception(JNIEnv* env, int errcode, const char* sqlite3Message, const char* message) { const char* exceptionClass; switch (errcode & 0xff) { /* mask off extended error code */ case SQLITE_IOERR: - exceptionClass = "org/sqlite/database/sqlite/SQLiteDiskIOException"; + exceptionClass = "android/database/sqlite/SQLiteDiskIOException"; break; case SQLITE_CORRUPT: case SQLITE_NOTADB: // treat "unsupported file format" error as corruption also - exceptionClass = "org/sqlite/database/sqlite/SQLiteDatabaseCorruptException"; + exceptionClass = "android/database/sqlite/SQLiteDatabaseCorruptException"; break; case SQLITE_CONSTRAINT: - exceptionClass = "org/sqlite/database/sqlite/SQLiteConstraintException"; + exceptionClass = "android/database/sqlite/SQLiteConstraintException"; break; case SQLITE_ABORT: - exceptionClass = "org/sqlite/database/sqlite/SQLiteAbortException"; + exceptionClass = "android/database/sqlite/SQLiteAbortException"; break; case SQLITE_DONE: - exceptionClass = "org/sqlite/database/sqlite/SQLiteDoneException"; + exceptionClass = "android/database/sqlite/SQLiteDoneException"; sqlite3Message = NULL; // SQLite error message is irrelevant in this case break; case SQLITE_FULL: - exceptionClass = "org/sqlite/database/sqlite/SQLiteFullException"; + exceptionClass = "android/database/sqlite/SQLiteFullException"; break; case SQLITE_MISUSE: - exceptionClass = "org/sqlite/database/sqlite/SQLiteMisuseException"; + exceptionClass = "android/database/sqlite/SQLiteMisuseException"; break; case SQLITE_PERM: - exceptionClass = "org/sqlite/database/sqlite/SQLiteAccessPermException"; + exceptionClass = "android/database/sqlite/SQLiteAccessPermException"; break; case SQLITE_BUSY: - exceptionClass = "org/sqlite/database/sqlite/SQLiteDatabaseLockedException"; + exceptionClass = "android/database/sqlite/SQLiteDatabaseLockedException"; break; case SQLITE_LOCKED: - exceptionClass = "org/sqlite/database/sqlite/SQLiteTableLockedException"; + exceptionClass = "android/database/sqlite/SQLiteTableLockedException"; break; case SQLITE_READONLY: - exceptionClass = "org/sqlite/database/sqlite/SQLiteReadOnlyDatabaseException"; + exceptionClass = "android/database/sqlite/SQLiteReadOnlyDatabaseException"; break; case SQLITE_CANTOPEN: - exceptionClass = "org/sqlite/database/sqlite/SQLiteCantOpenDatabaseException"; + exceptionClass = "android/database/sqlite/SQLiteCantOpenDatabaseException"; break; case SQLITE_TOOBIG: - exceptionClass = "org/sqlite/database/sqlite/SQLiteBlobTooBigException"; + exceptionClass = "android/database/sqlite/SQLiteBlobTooBigException"; break; case SQLITE_RANGE: - exceptionClass = "org/sqlite/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException"; + exceptionClass = "android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException"; break; case SQLITE_NOMEM: - exceptionClass = "org/sqlite/database/sqlite/SQLiteOutOfMemoryException"; + exceptionClass = "android/database/sqlite/SQLiteOutOfMemoryException"; break; case SQLITE_MISMATCH: - exceptionClass = "org/sqlite/database/sqlite/SQLiteDatatypeMismatchException"; + exceptionClass = "android/database/sqlite/SQLiteDatatypeMismatchException"; break; case SQLITE_INTERRUPT: exceptionClass = "android/os/OperationCanceledException"; break; default: - exceptionClass = "org/sqlite/database/sqlite/SQLiteException"; + exceptionClass = "android/database/sqlite/SQLiteException"; break; } if (sqlite3Message) { - char *zFullmsg = sqlite3_mprintf( - "%s (code %d)%s%s", sqlite3Message, errcode, - (message ? ": " : ""), (message ? message : "") - ); - jniThrowException(env, exceptionClass, zFullmsg); - sqlite3_free(zFullmsg); + String8 fullMessage; + fullMessage.append(sqlite3Message); + fullMessage.appendFormat(" (code %d)", errcode); // print extended error code + if (message) { + fullMessage.append(": "); + fullMessage.append(message); + } + jniThrowException(env, exceptionClass, fullMessage.string()); } else { jniThrowException(env, exceptionClass, message); } } } // namespace android Index: sqlite3/src/main/jni/sqlite/android_database_SQLiteCommon.h ================================================================== --- sqlite3/src/main/jni/sqlite/android_database_SQLiteCommon.h +++ sqlite3/src/main/jni/sqlite/android_database_SQLiteCommon.h @@ -11,20 +11,16 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ #ifndef _ANDROID_DATABASE_SQLITE_COMMON_H #define _ANDROID_DATABASE_SQLITE_COMMON_H #include -#include +#include #include // Special log tags defined in SQLiteDebug.java. #define SQLITE_LOG_TAG "SQLiteLog" Index: sqlite3/src/main/jni/sqlite/android_database_SQLiteConnection.cpp ================================================================== --- sqlite3/src/main/jni/sqlite/android_database_SQLiteConnection.cpp +++ sqlite3/src/main/jni/sqlite/android_database_SQLiteConnection.cpp @@ -11,39 +11,35 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ #define LOG_TAG "SQLiteConnection" #include -#include -#include "ALog-priv.h" +#include +#include +#include - +#include +#include +#include +#include #include + #include #include -#include -#if 0 #include -#endif #include -#if 0 #include -#endif #include "android_database_SQLiteCommon.h" -#include +#include "core_jni_helpers.h" // Set to 1 to use UTF16 storage for localized indexes. #define UTF16_STORAGE 0 namespace android { @@ -60,12 +56,10 @@ * operations but not so long as to cause the application to hang indefinitely if * there is a problem acquiring a database lock. */ static const int BUSY_TIMEOUT_MS = 2500; -static JavaVM *gpJavaVM = 0; - static struct { jfieldID name; jfieldID numArgs; jmethodID dispatchCallback; } gSQLiteCustomFunctionClassInfo; @@ -85,63 +79,39 @@ CREATE_IF_NECESSARY = 0x10000000, }; sqlite3* const db; const int openFlags; - std::string path; - std::string label; + const String8 path; + const String8 label; volatile bool canceled; - SQLiteConnection(sqlite3* db, int openFlags, const std::string& path, const std::string& label) : + SQLiteConnection(sqlite3* db, int openFlags, const String8& path, const String8& label) : db(db), openFlags(openFlags), path(path), label(label), canceled(false) { } }; // Called each time a statement begins execution, when tracing is enabled. static void sqliteTraceCallback(void *data, const char *sql) { SQLiteConnection* connection = static_cast(data); ALOG(LOG_VERBOSE, SQLITE_TRACE_TAG, "%s: \"%s\"\n", - connection->label.c_str(), sql); + connection->label.string(), sql); } // Called each time a statement finishes execution, when profiling is enabled. static void sqliteProfileCallback(void *data, const char *sql, sqlite3_uint64 tm) { SQLiteConnection* connection = static_cast(data); ALOG(LOG_VERBOSE, SQLITE_PROFILE_TAG, "%s: \"%s\" took %0.3f ms\n", - connection->label.c_str(), sql, tm * 0.000001f); + connection->label.string(), sql, tm * 0.000001f); } // Called after each SQLite VM instruction when cancelation is enabled. static int sqliteProgressHandlerCallback(void* data) { SQLiteConnection* connection = static_cast(data); return connection->canceled; } -/* -** This function is a collation sequence callback equivalent to the built-in -** BINARY sequence. -** -** Stock Android uses a modified version of sqlite3.c that calls out to a module -** named "sqlite3_android" to add extra built-in collations and functions to -** all database handles. Specifically, collation sequence "LOCALIZED". For now, -** this module does not include sqlite3_android (since it is difficult to build -** with the NDK only). Instead, this function is registered as "LOCALIZED" for all -** new database handles. -*/ -static int coll_localized( - void *not_used, - int nKey1, const void *pKey1, - int nKey2, const void *pKey2 -){ - int rc, n; - n = nKey1GetStringUTFChars(pathStr, NULL); - std::string path(pathChars); + String8 path(pathChars); env->ReleaseStringUTFChars(pathStr, pathChars); const char* labelChars = env->GetStringUTFChars(labelStr, NULL); - std::string label(labelChars); + String8 label(labelChars); env->ReleaseStringUTFChars(labelStr, labelChars); sqlite3* db; - int err = sqlite3_open_v2(path.c_str(), &db, sqliteFlags, NULL); + int err = sqlite3_open_v2(path.string(), &db, sqliteFlags, NULL); if (err != SQLITE_OK) { throw_sqlite3_exception_errcode(env, err, "Could not open database"); return 0; } - err = sqlite3_create_collation(db, "localized", SQLITE_UTF8, 0, coll_localized); - if (err != SQLITE_OK) { - throw_sqlite3_exception_errcode(env, err, "Could not register collation"); - sqlite3_close(db); - return 0; - } // Check that the database is really read/write when that is what we asked for. if ((sqliteFlags & SQLITE_OPEN_READWRITE) && sqlite3_db_readonly(db, NULL)) { throw_sqlite3_exception(env, db, "Could not open the database in read/write mode."); sqlite3_close(db); @@ -187,18 +151,16 @@ sqlite3_close(db); return 0; } // Register custom Android functions. -#if 0 err = register_android_functions(db, UTF16_STORAGE); if (err) { throw_sqlite3_exception(env, db, "Could not register Android SQL functions."); sqlite3_close(db); return 0; } -#endif // Create wrapper object. SQLiteConnection* connection = new SQLiteConnection(db, openFlags, path, label); // Enable tracing and profiling if requested. @@ -207,11 +169,11 @@ } if (enableProfile) { sqlite3_profile(db, &sqliteProfileCallback, connection); } - ALOGV("Opened connection %p with label '%s'", db, label.c_str()); + ALOGV("Opened connection %p with label '%s'", db, label.string()); return reinterpret_cast(connection); } static void nativeClose(JNIEnv* env, jclass clazz, jlong connectionPtr) { SQLiteConnection* connection = reinterpret_cast(connectionPtr); @@ -231,13 +193,11 @@ } // Called each time a custom function is evaluated. static void sqliteCustomFunctionCallback(sqlite3_context *context, int argc, sqlite3_value **argv) { - - JNIEnv* env = 0; - gpJavaVM->GetEnv((void**)&env, JNI_VERSION_1_4); + JNIEnv* env = AndroidRuntime::getJNIEnv(); // Get the callback function object. // Create a new local reference to it in case the callback tries to do something // dumb like unregister the function (thereby destroying the global ref) while it is running. jobject functionObjGlobal = reinterpret_cast(sqlite3_user_data(context)); @@ -270,20 +230,20 @@ env->DeleteLocalRef(functionObj); if (env->ExceptionCheck()) { ALOGE("An exception was thrown by custom SQLite function."); - /* LOGE_EX(env); */ + LOGE_EX(env); env->ExceptionClear(); } } // Called when a custom function is destroyed. static void sqliteCustomFunctionDestructor(void* data) { jobject functionObjGlobal = reinterpret_cast(data); - JNIEnv* env = 0; - gpJavaVM->GetEnv((void**)&env, JNI_VERSION_1_4); + + JNIEnv* env = AndroidRuntime::getJNIEnv(); env->DeleteGlobalRef(functionObjGlobal); } static void nativeRegisterCustomFunction(JNIEnv* env, jclass clazz, jlong connectionPtr, jobject functionObj) { @@ -312,18 +272,16 @@ static void nativeRegisterLocalizedCollators(JNIEnv* env, jclass clazz, jlong connectionPtr, jstring localeStr) { SQLiteConnection* connection = reinterpret_cast(connectionPtr); const char* locale = env->GetStringUTFChars(localeStr, NULL); -#if 0 int err = register_localized_collators(connection->db, locale, UTF16_STORAGE); env->ReleaseStringUTFChars(localeStr, locale); if (err != SQLITE_OK) { throw_sqlite3_exception(env, connection->db); } -#endif } static jlong nativePrepareStatement(JNIEnv* env, jclass clazz, jlong connectionPtr, jstring sqlString) { SQLiteConnection* connection = reinterpret_cast(connectionPtr); @@ -367,35 +325,31 @@ sqlite3_finalize(statement); } static jint nativeGetParameterCount(JNIEnv* env, jclass clazz, jlong connectionPtr, jlong statementPtr) { - SQLiteConnection* connection = reinterpret_cast(connectionPtr); sqlite3_stmt* statement = reinterpret_cast(statementPtr); return sqlite3_bind_parameter_count(statement); } static jboolean nativeIsReadOnly(JNIEnv* env, jclass clazz, jlong connectionPtr, jlong statementPtr) { - SQLiteConnection* connection = reinterpret_cast(connectionPtr); sqlite3_stmt* statement = reinterpret_cast(statementPtr); return sqlite3_stmt_readonly(statement) != 0; } static jint nativeGetColumnCount(JNIEnv* env, jclass clazz, jlong connectionPtr, jlong statementPtr) { - SQLiteConnection* connection = reinterpret_cast(connectionPtr); sqlite3_stmt* statement = reinterpret_cast(statementPtr); return sqlite3_column_count(statement); } static jstring nativeGetColumnName(JNIEnv* env, jclass clazz, jlong connectionPtr, jlong statementPtr, jint index) { - SQLiteConnection* connection = reinterpret_cast(connectionPtr); sqlite3_stmt* statement = reinterpret_cast(statementPtr); const jchar* name = static_cast(sqlite3_column_name16(statement, index)); if (name) { size_t length = 0; @@ -556,11 +510,10 @@ } return NULL; } static int createAshmemRegionWithData(JNIEnv* env, const void* data, size_t length) { -#if 0 int error = 0; int fd = ashmem_create_region(NULL, length); if (fd < 0) { error = errno; ALOGE("ashmem_create_region failed: %s", strerror(error)); @@ -586,12 +539,11 @@ } close(fd); } -#endif - jniThrowIOException(env, -1); + jniThrowIOException(env, error); return -1; } static jint nativeExecuteForBlobFileDescriptor(JNIEnv* env, jclass clazz, jlong connectionPtr, jlong statementPtr) { @@ -609,228 +561,203 @@ } } return -1; } -/* -** Note: The following symbols must be in the same order as the corresponding -** elements in the aMethod[] array in function nativeExecuteForCursorWindow(). -*/ -enum CWMethodNames { - CW_CLEAR = 0, - CW_SETNUMCOLUMNS = 1, - CW_ALLOCROW = 2, - CW_FREELASTROW = 3, - CW_PUTNULL = 4, - CW_PUTLONG = 5, - CW_PUTDOUBLE = 6, - CW_PUTSTRING = 7, - CW_PUTBLOB = 8 -}; - -/* -** An instance of this structure represents a single CursorWindow java method. -*/ -struct CWMethod { - jmethodID id; /* Method id */ - const char *zName; /* Method name */ - const char *zSig; /* Method JNI signature */ -}; - -/* -** Append the contents of the row that SQL statement pStmt currently points to -** to the CursorWindow object passed as the second argument. The CursorWindow -** currently contains iRow rows. Return true on success or false if an error -** occurs. -*/ -static jboolean copyRowToWindow( - JNIEnv *pEnv, - jobject win, - int iRow, - sqlite3_stmt *pStmt, - CWMethod *aMethod -){ - int nCol = sqlite3_column_count(pStmt); - int i; - jboolean bOk; - - bOk = pEnv->CallBooleanMethod(win, aMethod[CW_ALLOCROW].id); - for(i=0; bOk && iCallBooleanMethod(win, aMethod[CW_PUTNULL].id, iRow, i); - break; - } - - case SQLITE_INTEGER: { - jlong val = sqlite3_column_int64(pStmt, i); - bOk = pEnv->CallBooleanMethod(win, aMethod[CW_PUTLONG].id, val, iRow, i); - break; - } - - case SQLITE_FLOAT: { - jdouble val = sqlite3_column_double(pStmt, i); - bOk = pEnv->CallBooleanMethod(win, aMethod[CW_PUTDOUBLE].id, val, iRow, i); - break; - } - - case SQLITE_TEXT: { - jchar *pStr = (jchar*)sqlite3_column_text16(pStmt, i); - int nStr = sqlite3_column_bytes16(pStmt, i) / sizeof(jchar); - jstring val = pEnv->NewString(pStr, nStr); - bOk = pEnv->CallBooleanMethod(win, aMethod[CW_PUTSTRING].id, val, iRow, i); - pEnv->DeleteLocalRef(val); - break; - } - - default: { - assert( sqlite3_column_type(pStmt, i)==SQLITE_BLOB ); - const jbyte *p = (const jbyte*)sqlite3_column_blob(pStmt, i); - int n = sqlite3_column_bytes(pStmt, i); - jbyteArray val = pEnv->NewByteArray(n); - pEnv->SetByteArrayRegion(val, 0, n, p); - bOk = pEnv->CallBooleanMethod(win, aMethod[CW_PUTBLOB].id, val, iRow, i); - pEnv->DeleteLocalRef(val); - break; - } - } - - if( bOk==0 ){ - pEnv->CallVoidMethod(win, aMethod[CW_FREELASTROW].id); - } - } - - return bOk; -} - -static jboolean setWindowNumColumns( - JNIEnv *pEnv, - jobject win, - sqlite3_stmt *pStmt, - CWMethod *aMethod -){ - int nCol; - - pEnv->CallVoidMethod(win, aMethod[CW_CLEAR].id); - nCol = sqlite3_column_count(pStmt); - return pEnv->CallBooleanMethod(win, aMethod[CW_SETNUMCOLUMNS].id, (jint)nCol); -} - -/* -** This method has been rewritten for org.sqlite.database.*. The original -** android implementation used the C++ interface to populate a CursorWindow -** object. Since the NDK does not export this interface, we invoke the Java -** interface using standard JNI methods to do the same thing. -** -** This function executes the SQLite statement object passed as the 4th -** argument and copies one or more returned rows into the CursorWindow -** object passed as the 5th argument. The set of rows copied into the -** CursorWindow is always contiguous. -** -** The only row that *must* be copied into the CursorWindow is row -** iRowRequired. Ideally, all rows from iRowStart through to the end -** of the query are copied into the CursorWindow. If this is not possible -** (CursorWindow objects have a finite capacity), some compromise position -** is found (see comments embedded in the code below for details). -** -** The return value is a 64-bit integer calculated as follows: -** -** (iStart << 32) | nRow -** -** where iStart is the index of the first row copied into the CursorWindow. -** If the countAllRows argument is true, nRow is the total number of rows -** returned by the query. Otherwise, nRow is one greater than the index of -** the last row copied into the CursorWindow. -*/ -static jlong nativeExecuteForCursorWindow( - JNIEnv *pEnv, - jclass clazz, - jlong connectionPtr, /* Pointer to SQLiteConnection C++ object */ - jlong statementPtr, /* Pointer to sqlite3_stmt object */ - jobject win, /* The CursorWindow object to populate */ - jint startPos, /* First row to add (advisory) */ - jint iRowRequired, /* Required row */ - jboolean countAllRows -) { - SQLiteConnection *pConnection = reinterpret_cast(connectionPtr); - sqlite3_stmt *pStmt = reinterpret_cast(statementPtr); - - CWMethod aMethod[] = { - {0, "clear", "()V"}, - {0, "setNumColumns", "(I)Z"}, - {0, "allocRow", "()Z"}, - {0, "freeLastRow", "()V"}, - {0, "putNull", "(II)Z"}, - {0, "putLong", "(JII)Z"}, - {0, "putDouble", "(DII)Z"}, - {0, "putString", "(Ljava/lang/String;II)Z"}, - {0, "putBlob", "([BII)Z"}, - }; - jclass cls; /* Class android.database.CursorWindow */ - int i; /* Iterator variable */ - int nCol; /* Number of columns returned by pStmt */ - int nRow; - jboolean bOk; - int iStart; /* First row copied to CursorWindow */ - - /* Locate all required CursorWindow methods. */ - cls = pEnv->FindClass("android/database/CursorWindow"); - for(i=0; i<(sizeof(aMethod)/sizeof(struct CWMethod)); i++){ - aMethod[i].id = pEnv->GetMethodID(cls, aMethod[i].zName, aMethod[i].zSig); - if( aMethod[i].id==NULL ){ - jniThrowExceptionFmt(pEnv, "java/lang/Exception", - "Failed to find method CursorWindow.%s()", aMethod[i].zName - ); - return 0; - } - } - - - /* Set the number of columns in the window */ - bOk = setWindowNumColumns(pEnv, win, pStmt, aMethod); - if( bOk==0 ) return 0; - - nRow = 0; - iStart = startPos; - while( sqlite3_step(pStmt)==SQLITE_ROW ){ - /* Only copy in rows that occur at or after row index iStart. */ - if( nRow>=iStart && bOk ){ - bOk = copyRowToWindow(pEnv, win, (nRow - iStart), pStmt, aMethod); - if( bOk==0 ){ - /* The CursorWindow object ran out of memory. If row iRowRequired was - ** not successfully added before this happened, clear the CursorWindow - ** and try to add the current row again. */ - if( nRow<=iRowRequired ){ - bOk = setWindowNumColumns(pEnv, win, pStmt, aMethod); - if( bOk==0 ){ - sqlite3_reset(pStmt); - return 0; - } - iStart = nRow; - bOk = copyRowToWindow(pEnv, win, (nRow - iStart), pStmt, aMethod); - } - - /* If the CursorWindow is still full and the countAllRows flag is not - ** set, break out of the loop here. If countAllRows is set, continue - ** so as to set variable nRow correctly. */ - if( bOk==0 && countAllRows==0 ) break; - } - } - - nRow++; - } - - /* Finalize the statement. If this indicates an error occurred, throw an - ** SQLiteException exception. */ - int rc = sqlite3_reset(pStmt); - if( rc!=SQLITE_OK ){ - throw_sqlite3_exception(pEnv, sqlite3_db_handle(pStmt)); - return 0; - } - - jlong lRet = jlong(iStart) << 32 | jlong(nRow); - return lRet; +enum CopyRowResult { + CPR_OK, + CPR_FULL, + CPR_ERROR, +}; + +static CopyRowResult copyRow(JNIEnv* env, CursorWindow* window, + sqlite3_stmt* statement, int numColumns, int startPos, int addedRows) { + // Allocate a new field directory for the row. + status_t status = window->allocRow(); + if (status) { + LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d, error=%d", + startPos, addedRows, status); + return CPR_FULL; + } + + // Pack the row into the window. + CopyRowResult result = CPR_OK; + for (int i = 0; i < numColumns; i++) { + int type = sqlite3_column_type(statement, i); + if (type == SQLITE_TEXT) { + // TEXT data + const char* text = reinterpret_cast( + sqlite3_column_text(statement, i)); + // SQLite does not include the NULL terminator in size, but does + // ensure all strings are NULL terminated, so increase size by + // one to make sure we store the terminator. + size_t sizeIncludingNull = sqlite3_column_bytes(statement, i) + 1; + status = window->putString(addedRows, i, text, sizeIncludingNull); + if (status) { + LOG_WINDOW("Failed allocating %u bytes for text at %d,%d, error=%d", + sizeIncludingNull, startPos + addedRows, i, status); + result = CPR_FULL; + break; + } + LOG_WINDOW("%d,%d is TEXT with %u bytes", + startPos + addedRows, i, sizeIncludingNull); + } else if (type == SQLITE_INTEGER) { + // INTEGER data + int64_t value = sqlite3_column_int64(statement, i); + status = window->putLong(addedRows, i, value); + if (status) { + LOG_WINDOW("Failed allocating space for a long in column %d, error=%d", + i, status); + result = CPR_FULL; + break; + } + LOG_WINDOW("%d,%d is INTEGER 0x%016llx", startPos + addedRows, i, value); + } else if (type == SQLITE_FLOAT) { + // FLOAT data + double value = sqlite3_column_double(statement, i); + status = window->putDouble(addedRows, i, value); + if (status) { + LOG_WINDOW("Failed allocating space for a double in column %d, error=%d", + i, status); + result = CPR_FULL; + break; + } + LOG_WINDOW("%d,%d is FLOAT %lf", startPos + addedRows, i, value); + } else if (type == SQLITE_BLOB) { + // BLOB data + const void* blob = sqlite3_column_blob(statement, i); + size_t size = sqlite3_column_bytes(statement, i); + status = window->putBlob(addedRows, i, blob, size); + if (status) { + LOG_WINDOW("Failed allocating %u bytes for blob at %d,%d, error=%d", + size, startPos + addedRows, i, status); + result = CPR_FULL; + break; + } + LOG_WINDOW("%d,%d is Blob with %u bytes", + startPos + addedRows, i, size); + } else if (type == SQLITE_NULL) { + // NULL field + status = window->putNull(addedRows, i); + if (status) { + LOG_WINDOW("Failed allocating space for a null in column %d, error=%d", + i, status); + result = CPR_FULL; + break; + } + + LOG_WINDOW("%d,%d is NULL", startPos + addedRows, i); + } else { + // Unknown data + ALOGE("Unknown column type when filling database window"); + throw_sqlite3_exception(env, "Unknown column type when filling window"); + result = CPR_ERROR; + break; + } + } + + // Free the last row if if was not successfully copied. + if (result != CPR_OK) { + window->freeLastRow(); + } + return result; +} + +static jlong nativeExecuteForCursorWindow(JNIEnv* env, jclass clazz, + jlong connectionPtr, jlong statementPtr, jlong windowPtr, + jint startPos, jint requiredPos, jboolean countAllRows) { + SQLiteConnection* connection = reinterpret_cast(connectionPtr); + sqlite3_stmt* statement = reinterpret_cast(statementPtr); + CursorWindow* window = reinterpret_cast(windowPtr); + + status_t status = window->clear(); + if (status) { + String8 msg; + msg.appendFormat("Failed to clear the cursor window, status=%d", status); + throw_sqlite3_exception(env, connection->db, msg.string()); + return 0; + } + + int numColumns = sqlite3_column_count(statement); + status = window->setNumColumns(numColumns); + if (status) { + String8 msg; + msg.appendFormat("Failed to set the cursor window column count to %d, status=%d", + numColumns, status); + throw_sqlite3_exception(env, connection->db, msg.string()); + return 0; + } + + int retryCount = 0; + int totalRows = 0; + int addedRows = 0; + bool windowFull = false; + bool gotException = false; + while (!gotException && (!windowFull || countAllRows)) { + int err = sqlite3_step(statement); + if (err == SQLITE_ROW) { + LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows); + retryCount = 0; + totalRows += 1; + + // Skip the row if the window is full or we haven't reached the start position yet. + if (startPos >= totalRows || windowFull) { + continue; + } + + CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows); + if (cpr == CPR_FULL && addedRows && startPos + addedRows <= requiredPos) { + // We filled the window before we got to the one row that we really wanted. + // Clear the window and start filling it again from here. + // TODO: Would be nicer if we could progressively replace earlier rows. + window->clear(); + window->setNumColumns(numColumns); + startPos += addedRows; + addedRows = 0; + cpr = copyRow(env, window, statement, numColumns, startPos, addedRows); + } + + if (cpr == CPR_OK) { + addedRows += 1; + } else if (cpr == CPR_FULL) { + windowFull = true; + } else { + gotException = true; + } + } else if (err == SQLITE_DONE) { + // All rows processed, bail + LOG_WINDOW("Processed all rows"); + break; + } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) { + // The table is locked, retry + LOG_WINDOW("Database locked, retrying"); + if (retryCount > 50) { + ALOGE("Bailing on database busy retry"); + throw_sqlite3_exception(env, connection->db, "retrycount exceeded"); + gotException = true; + } else { + // Sleep to give the thread holding the lock a chance to finish + usleep(1000); + retryCount++; + } + } else { + throw_sqlite3_exception(env, connection->db); + gotException = true; + } + } + + LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows" + "to the window in %d bytes", + statement, totalRows, addedRows, window->size() - window->freeSpace()); + sqlite3_reset(statement); + + // Report the total number of rows on request. + if (startPos > totalRows) { + ALOGE("startPos %d > actual rows %d", startPos, totalRows); + } + jlong result = jlong(startPos) << 32 | jlong(totalRows); + return result; } static jint nativeGetDbLookaside(JNIEnv* env, jobject clazz, jlong connectionPtr) { SQLiteConnection* connection = reinterpret_cast(connectionPtr); @@ -856,27 +783,19 @@ } else { sqlite3_progress_handler(connection->db, 0, NULL, NULL); } } -static jboolean nativeHasCodec(JNIEnv* env, jobject clazz){ -#ifdef SQLITE_HAS_CODEC - return true; -#else - return false; -#endif -} - - -static JNINativeMethod sMethods[] = + +static const JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ { "nativeOpen", "(Ljava/lang/String;ILjava/lang/String;ZZ)J", (void*)nativeOpen }, { "nativeClose", "(J)V", (void*)nativeClose }, - { "nativeRegisterCustomFunction", "(JLorg/sqlite/database/sqlite/SQLiteCustomFunction;)V", + { "nativeRegisterCustomFunction", "(JLandroid/database/sqlite/SQLiteCustomFunction;)V", (void*)nativeRegisterCustomFunction }, { "nativeRegisterLocalizedCollators", "(JLjava/lang/String;)V", (void*)nativeRegisterLocalizedCollators }, { "nativePrepareStatement", "(JLjava/lang/String;)J", (void*)nativePrepareStatement }, @@ -912,70 +831,32 @@ (void*)nativeExecuteForBlobFileDescriptor }, { "nativeExecuteForChangedRowCount", "(JJ)I", (void*)nativeExecuteForChangedRowCount }, { "nativeExecuteForLastInsertedRowId", "(JJ)J", (void*)nativeExecuteForLastInsertedRowId }, - { "nativeExecuteForCursorWindow", "(JJLandroid/database/CursorWindow;IIZ)J", + { "nativeExecuteForCursorWindow", "(JJJIIZ)J", (void*)nativeExecuteForCursorWindow }, { "nativeGetDbLookaside", "(J)I", (void*)nativeGetDbLookaside }, { "nativeCancel", "(J)V", (void*)nativeCancel }, { "nativeResetCancel", "(JZ)V", (void*)nativeResetCancel }, - - { "nativeHasCodec", "()Z", (void*)nativeHasCodec }, }; -#define FIND_CLASS(var, className) \ - var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); - -#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ - var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ - LOG_FATAL_IF(! var, "Unable to find method" methodName); - -#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ - var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ - LOG_FATAL_IF(! var, "Unable to find field " fieldName); - int register_android_database_SQLiteConnection(JNIEnv *env) { - jclass clazz; - FIND_CLASS(clazz, "org/sqlite/database/sqlite/SQLiteCustomFunction"); - - GET_FIELD_ID(gSQLiteCustomFunctionClassInfo.name, clazz, - "name", "Ljava/lang/String;"); - GET_FIELD_ID(gSQLiteCustomFunctionClassInfo.numArgs, clazz, - "numArgs", "I"); - GET_METHOD_ID(gSQLiteCustomFunctionClassInfo.dispatchCallback, - clazz, "dispatchCallback", "([Ljava/lang/String;)V"); - - FIND_CLASS(clazz, "java/lang/String"); - gStringClassInfo.clazz = jclass(env->NewGlobalRef(clazz)); - - return jniRegisterNativeMethods(env, - "org/sqlite/database/sqlite/SQLiteConnection", - sMethods, NELEM(sMethods) - ); -} - -extern int register_android_database_SQLiteGlobal(JNIEnv *env); -extern int register_android_database_SQLiteDebug(JNIEnv *env); - -} // namespace android - -extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { - JNIEnv *env = 0; - - android::gpJavaVM = vm; - vm->GetEnv((void**)&env, JNI_VERSION_1_4); - - android::register_android_database_SQLiteConnection(env); - android::register_android_database_SQLiteDebug(env); - android::register_android_database_SQLiteGlobal(env); - - return JNI_VERSION_1_4; -} - - - + jclass clazz = FindClassOrDie(env, "android/database/sqlite/SQLiteCustomFunction"); + + gSQLiteCustomFunctionClassInfo.name = GetFieldIDOrDie(env, clazz, "name", "Ljava/lang/String;"); + gSQLiteCustomFunctionClassInfo.numArgs = GetFieldIDOrDie(env, clazz, "numArgs", "I"); + gSQLiteCustomFunctionClassInfo.dispatchCallback = GetMethodIDOrDie(env, clazz, + "dispatchCallback", "([Ljava/lang/String;)V"); + + clazz = FindClassOrDie(env, "java/lang/String"); + gStringClassInfo.clazz = MakeGlobalRefOrDie(env, clazz); + + return RegisterMethodsOrDie(env, "android/database/sqlite/SQLiteConnection", sMethods, + NELEM(sMethods)); +} + +} // namespace android Index: sqlite3/src/main/jni/sqlite/android_database_SQLiteDebug.cpp ================================================================== --- sqlite3/src/main/jni/sqlite/android_database_SQLiteDebug.cpp +++ sqlite3/src/main/jni/sqlite/android_database_SQLiteDebug.cpp @@ -11,27 +11,26 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ #define LOG_TAG "SQLiteDebug" #include -#include -#include +#include +#include #include #include #include #include +#include #include + +#include "core_jni_helpers.h" namespace android { static struct { jfieldID memoryUsed; @@ -57,36 +56,26 @@ /* * JNI registration. */ -static JNINativeMethod gMethods[] = +static const JNINativeMethod gMethods[] = { - { "nativeGetPagerStats", "(Lorg/sqlite/database/sqlite/SQLiteDebug$PagerStats;)V", + { "nativeGetPagerStats", "(Landroid/database/sqlite/SQLiteDebug$PagerStats;)V", (void*) nativeGetPagerStats }, }; -#define FIND_CLASS(var, className) \ - var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); - -#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ - var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ - LOG_FATAL_IF(! var, "Unable to find field " fieldName); - int register_android_database_SQLiteDebug(JNIEnv *env) { - jclass clazz; - FIND_CLASS(clazz, "org/sqlite/database/sqlite/SQLiteDebug$PagerStats"); - - GET_FIELD_ID(gSQLiteDebugPagerStatsClassInfo.memoryUsed, clazz, - "memoryUsed", "I"); - GET_FIELD_ID(gSQLiteDebugPagerStatsClassInfo.largestMemAlloc, clazz, - "largestMemAlloc", "I"); - GET_FIELD_ID(gSQLiteDebugPagerStatsClassInfo.pageCacheOverflow, clazz, + jclass clazz = FindClassOrDie(env, "android/database/sqlite/SQLiteDebug$PagerStats"); + + gSQLiteDebugPagerStatsClassInfo.memoryUsed = GetFieldIDOrDie(env, clazz, "memoryUsed", "I"); + gSQLiteDebugPagerStatsClassInfo.largestMemAlloc = GetFieldIDOrDie(env, clazz, + "largestMemAlloc", "I"); + gSQLiteDebugPagerStatsClassInfo.pageCacheOverflow = GetFieldIDOrDie(env, clazz, "pageCacheOverflow", "I"); - return jniRegisterNativeMethods(env, "org/sqlite/database/sqlite/SQLiteDebug", + return RegisterMethodsOrDie(env, "android/database/sqlite/SQLiteDebug", gMethods, NELEM(gMethods)); } } // namespace android Index: sqlite3/src/main/jni/sqlite/android_database_SQLiteGlobal.cpp ================================================================== --- sqlite3/src/main/jni/sqlite/android_database_SQLiteGlobal.cpp +++ sqlite3/src/main/jni/sqlite/android_database_SQLiteGlobal.cpp @@ -11,29 +11,22 @@ * 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. */ -/* -** Modified to support SQLite extensions by the SQLite developers: -** sqlite-dev@sqlite.org. -*/ #define LOG_TAG "SQLiteGlobal" #include -#include -#include "ALog-priv.h" - +#include +#include "core_jni_helpers.h" #include -#if 0 #include -#endif - #include "android_database_SQLiteCommon.h" +#include "android_util_Log.h" namespace android { // Limit heap to 8MB for now. This is 4 times the maximum cursor window // size, as has been used by the original code in SQLiteDatabase for @@ -40,18 +33,22 @@ // a long time. static const int SOFT_HEAP_LIMIT = 8 * 1024 * 1024; // Called each time a message is logged. -static void sqliteLogCallback(void* data, int iErrCode, const char* zMsg) { +static void sqliteLogCallback(void* data, int err, const char* msg) { bool verboseLog = !!data; - if (iErrCode == 0 || iErrCode == SQLITE_CONSTRAINT || iErrCode == SQLITE_SCHEMA) { + int errType = err & 255; + if (errType == 0 || errType == SQLITE_CONSTRAINT || errType == SQLITE_SCHEMA + || errType == SQLITE_NOTICE || err == SQLITE_WARNING_AUTOINDEX) { if (verboseLog) { - ALOG(LOG_VERBOSE, SQLITE_LOG_TAG, "(%d) %s\n", iErrCode, zMsg); + ALOG(LOG_VERBOSE, SQLITE_LOG_TAG, "(%d) %s\n", err, msg); } + } else if (errType == SQLITE_WARNING) { + ALOG(LOG_WARN, SQLITE_LOG_TAG, "(%d) %s\n", err, msg); } else { - ALOG(LOG_ERROR, SQLITE_LOG_TAG, "(%d) %s\n", iErrCode, zMsg); + ALOG(LOG_ERROR, SQLITE_LOG_TAG, "(%d) %s\n", err, msg); } } // Sets the global SQLite configuration. // This must be called before any other SQLite functions are called. @@ -60,14 +57,11 @@ // threads as long as no two threads use the same database connection at the same // time (which we guarantee in the SQLite database wrappers). sqlite3_config(SQLITE_CONFIG_MULTITHREAD); // Redirect SQLite log messages to the Android log. -#if 0 bool verboseLog = android_util_Log_isVerboseLogEnabled(SQLITE_LOG_TAG); -#endif - bool verboseLog = false; sqlite3_config(SQLITE_CONFIG_LOG, &sqliteLogCallback, verboseLog ? (void*)1 : NULL); // The soft heap limit prevents the page cache allocations from growing // beyond the given limit, no matter what the max page cache sizes are // set to. The limit does not, as of 3.5.0, affect any other allocations. @@ -79,21 +73,20 @@ static jint nativeReleaseMemory(JNIEnv* env, jclass clazz) { return sqlite3_release_memory(SOFT_HEAP_LIMIT); } -static JNINativeMethod sMethods[] = +static const JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ - { "nativeReleaseMemory", "()I", - (void*)nativeReleaseMemory }, + { "nativeReleaseMemory", "()I", (void*)nativeReleaseMemory }, }; int register_android_database_SQLiteGlobal(JNIEnv *env) { sqliteInitialize(); - return jniRegisterNativeMethods(env, "org/sqlite/database/sqlite/SQLiteGlobal", + return RegisterMethodsOrDie(env, "android/database/sqlite/SQLiteGlobal", sMethods, NELEM(sMethods)); } } // namespace android