android change to handle Change-Id: Idbeed81b5b7349059e467b33a8641abf0b4aaeff
Change-Id: Icf221a8e8d4c281f7719875816835ad7dfe7f3d1
This commit is contained in:
@@ -144,6 +144,22 @@ import android.util.Log;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
synchronized(this) {
|
||||
StringBuilder buff = new StringBuilder();
|
||||
buff.append(" nStatement=");
|
||||
buff.append(nStatement);
|
||||
buff.append(", db=");
|
||||
buff.append(mDatabase.getPath());
|
||||
buff.append(", db_connectionNum=");
|
||||
buff.append(mDatabase.mConnectionNum);
|
||||
buff.append(", sql=");
|
||||
int len = mSqlStmt.length();
|
||||
buff.append(mSqlStmt.substring(0, (len > 100) ? 100 : len));
|
||||
return buff.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles SQL into a SQLite program.
|
||||
*
|
||||
|
||||
@@ -1066,7 +1066,7 @@ public class SQLiteDatabase extends SQLiteClosable {
|
||||
closePendingStatements();
|
||||
releaseCustomFunctions();
|
||||
// close this database instance - regardless of its reference count value
|
||||
dbclose();
|
||||
closeDatabase();
|
||||
if (mConnectionPool != null) {
|
||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||
assert mConnectionPool != null;
|
||||
@@ -1075,7 +1075,7 @@ public class SQLiteDatabase extends SQLiteClosable {
|
||||
mConnectionPool.close();
|
||||
}
|
||||
} finally {
|
||||
unlock();
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1099,6 +1099,47 @@ public class SQLiteDatabase extends SQLiteClosable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* package level access for testing purposes
|
||||
*/
|
||||
/* package */ void closeDatabase() throws SQLiteException {
|
||||
try {
|
||||
dbclose();
|
||||
} catch (SQLiteUnfinalizedObjectsException e) {
|
||||
String msg = e.getMessage();
|
||||
String[] tokens = msg.split(",", 2);
|
||||
int stmtId = Integer.parseInt(tokens[0]);
|
||||
// get extra info about this statement, if it is still to be released by closeClosable()
|
||||
Iterator<Map.Entry<SQLiteClosable, Object>> iter = mPrograms.entrySet().iterator();
|
||||
boolean found = false;
|
||||
while (iter.hasNext()) {
|
||||
Map.Entry<SQLiteClosable, Object> entry = iter.next();
|
||||
SQLiteClosable program = entry.getKey();
|
||||
if (program != null && program instanceof SQLiteProgram) {
|
||||
SQLiteCompiledSql compiledSql = ((SQLiteProgram)program).mCompiledSql;
|
||||
if (compiledSql.nStatement == stmtId) {
|
||||
msg = compiledSql.toString();
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// the statement is already released by closeClosable(). is it waiting to be
|
||||
// finalized?
|
||||
if (mClosedStatementIds.contains(stmtId)) {
|
||||
Log.w(TAG, "this shouldn't happen. finalizing the statement now: ");
|
||||
closePendingStatements();
|
||||
// try to close the database again
|
||||
closeDatabase();
|
||||
}
|
||||
} else {
|
||||
// the statement is not yet closed. most probably programming error in the app.
|
||||
Log.w(TAG, "dbclose failed due to un-close()d SQL statements: " + msg);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Native call to close the database.
|
||||
*/
|
||||
|
||||
@@ -52,7 +52,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
|
||||
/**
|
||||
* the SQLiteCompiledSql object for the given sql statement.
|
||||
*/
|
||||
private SQLiteCompiledSql mCompiledSql;
|
||||
/* package */ SQLiteCompiledSql mCompiledSql;
|
||||
|
||||
/**
|
||||
* SQLiteCompiledSql statement id is populated with the corresponding object from the above
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.database.sqlite;
|
||||
|
||||
/**
|
||||
* Thrown if the database can't be closed because of some un-closed
|
||||
* Cursor or SQLiteStatement objects. Could happen when a thread is trying to close
|
||||
* the database while another thread still hasn't closed a Cursor on that database.
|
||||
* @hide
|
||||
*/
|
||||
public class SQLiteUnfinalizedObjectsException extends SQLiteException {
|
||||
public SQLiteUnfinalizedObjectsException() {}
|
||||
|
||||
public SQLiteUnfinalizedObjectsException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
@@ -593,6 +593,9 @@ void throw_sqlite3_exception(JNIEnv* env, int errcode,
|
||||
case SQLITE_MISMATCH:
|
||||
exceptionClass = "android/database/sqlite/SQLiteDatatypeMismatchException";
|
||||
break;
|
||||
case SQLITE_UNCLOSED:
|
||||
exceptionClass = "android/database/sqlite/SQLiteUnfinalizedObjectsException";
|
||||
break;
|
||||
default:
|
||||
exceptionClass = "android/database/sqlite/SQLiteException";
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.database.sqlite;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabaseTest.ClassToTestSqlCompilationAndCaching;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class SQLiteUnfinalizedExceptionTest extends AndroidTestCase {
|
||||
private SQLiteDatabase mDatabase;
|
||||
private File mDatabaseFile;
|
||||
private static final String TABLE_NAME = "testCursor";
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
|
||||
mDatabaseFile = new File(dbDir, "UnfinalizedExceptionTest.db");
|
||||
if (mDatabaseFile.exists()) {
|
||||
mDatabaseFile.delete();
|
||||
}
|
||||
mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
|
||||
assertNotNull(mDatabase);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
mDatabase.close();
|
||||
mDatabaseFile.delete();
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testUnfinalizedExceptionNotExcpected() {
|
||||
mDatabase.execSQL("CREATE TABLE " + TABLE_NAME + " (i int, j int);");
|
||||
// the above statement should be in SQLiteDatabase.mPrograms
|
||||
// and should automatically be finalized when database is closed
|
||||
mDatabase.lock();
|
||||
try {
|
||||
mDatabase.closeDatabase();
|
||||
} finally {
|
||||
mDatabase.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testUnfinalizedException() {
|
||||
mDatabase.execSQL("CREATE TABLE " + TABLE_NAME + " (i int, j int);");
|
||||
mDatabase.lock();
|
||||
mDatabase.closePendingStatements(); // clears the above from finalizer queue in mdatabase
|
||||
mDatabase.unlock();
|
||||
ClassToTestSqlCompilationAndCaching.create(mDatabase, "select * from " + TABLE_NAME);
|
||||
// since the above is NOT closed, closing database should fail
|
||||
mDatabase.lock();
|
||||
try {
|
||||
mDatabase.closeDatabase();
|
||||
fail("exception expected");
|
||||
} catch (SQLiteUnfinalizedObjectsException e) {
|
||||
// expected
|
||||
} finally {
|
||||
mDatabase.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user