blob: 15e174d88cf157e8517bde09087b50129906d1cb [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.database.sqlite.cts;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Locale;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.SQLException;
import android.database.sqlite.SQLiteCursor;
import android.database.sqlite.SQLiteCursorDriver;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteQuery;
import android.database.sqlite.SQLiteStatement;
import android.database.sqlite.SQLiteTransactionListener;
import android.test.AndroidTestCase;
import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.SmallTest;
public class SQLiteDatabaseTest extends AndroidTestCase {
private SQLiteDatabase mDatabase;
private File mDatabaseFile;
private String mDatabaseFilePath;
private String mDatabaseDir;
private boolean mTransactionListenerOnBeginCalled;
private boolean mTransactionListenerOnCommitCalled;
private boolean mTransactionListenerOnRollbackCalled;
private static final String DATABASE_FILE_NAME = "database_test.db";
private static final String TABLE_NAME = "test";
private static final int COLUMN_ID_INDEX = 0;
private static final int COLUMN_NAME_INDEX = 1;
private static final int COLUMN_AGE_INDEX = 2;
private static final int COLUMN_ADDR_INDEX = 3;
private static final String[] TEST_PROJECTION = new String[] {
"_id", // 0
"name", // 1
"age", // 2
"address" // 3
};
@Override
protected void setUp() throws Exception {
super.setUp();
getContext().deleteDatabase(DATABASE_FILE_NAME);
mDatabaseFilePath = getContext().getDatabasePath(DATABASE_FILE_NAME).getPath();
mDatabaseFile = getContext().getDatabasePath(DATABASE_FILE_NAME);
mDatabaseDir = mDatabaseFile.getParent();
mDatabaseFile.getParentFile().mkdirs(); // directory may not exist
mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile, null);
assertNotNull(mDatabase);
mTransactionListenerOnBeginCalled = false;
mTransactionListenerOnCommitCalled = false;
mTransactionListenerOnRollbackCalled = false;
}
@Override
protected void tearDown() throws Exception {
mDatabase.close();
mDatabaseFile.delete();
super.tearDown();
}
public void testOpenDatabase() {
CursorFactory factory = new CursorFactory() {
public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
String editTable, SQLiteQuery query) {
return new MockSQLiteCursor(db, masterQuery, editTable, query);
}
};
SQLiteDatabase db = SQLiteDatabase.openDatabase(mDatabaseFilePath,
factory, SQLiteDatabase.CREATE_IF_NECESSARY);
assertNotNull(db);
db.close();
File dbFile = new File(mDatabaseDir, "database_test12345678.db");
dbFile.delete();
assertFalse(dbFile.exists());
db = SQLiteDatabase.openOrCreateDatabase(dbFile.getPath(), factory);
assertNotNull(db);
db.close();
dbFile.delete();
dbFile = new File(mDatabaseDir, DATABASE_FILE_NAME);
db = SQLiteDatabase.openOrCreateDatabase(dbFile, factory);
assertNotNull(db);
db.close();
dbFile.delete();
db = SQLiteDatabase.create(factory);
assertNotNull(db);
db.close();
}
public void testDeleteDatabase() throws IOException {
File dbFile = new File(mDatabaseDir, "database_test12345678.db");
File journalFile = new File(dbFile.getPath() + "-journal");
File shmFile = new File(dbFile.getPath() + "-shm");
File walFile = new File(dbFile.getPath() + "-wal");
File mjFile1 = new File(dbFile.getPath() + "-mj00000000");
File mjFile2 = new File(dbFile.getPath() + "-mj00000001");
File innocentFile = new File(dbFile.getPath() + "-innocent");
dbFile.createNewFile();
journalFile.createNewFile();
shmFile.createNewFile();
walFile.createNewFile();
mjFile1.createNewFile();
mjFile2.createNewFile();
innocentFile.createNewFile();
boolean deleted = SQLiteDatabase.deleteDatabase(dbFile);
assertTrue(deleted);
assertFalse(dbFile.exists());
assertFalse(journalFile.exists());
assertFalse(shmFile.exists());
assertFalse(walFile.exists());
assertFalse(mjFile1.exists());
assertFalse(mjFile2.exists());
assertTrue(innocentFile.exists());
innocentFile.delete();
boolean deletedAgain = SQLiteDatabase.deleteDatabase(dbFile);
assertFalse(deletedAgain);
}
private class MockSQLiteCursor extends SQLiteCursor {
public MockSQLiteCursor(SQLiteDatabase db, SQLiteCursorDriver driver,
String editTable, SQLiteQuery query) {
super(db, driver, editTable, query);
}
}
public void testTransaction() {
mDatabase.execSQL("CREATE TABLE test (num INTEGER);");
mDatabase.execSQL("INSERT INTO test (num) VALUES (0)");
// test execSQL without any explicit transactions.
setNum(1);
assertNum(1);
// Test a single-level transaction.
setNum(0);
assertFalse(mDatabase.inTransaction());
mDatabase.beginTransaction();
assertTrue(mDatabase.inTransaction());
setNum(1);
mDatabase.setTransactionSuccessful();
mDatabase.endTransaction();
assertFalse(mDatabase.inTransaction());
assertNum(1);
assertFalse(mDatabase.isDbLockedByCurrentThread());
assertFalse(mDatabase.isDbLockedByOtherThreads());
// Test a rolled-back transaction.
setNum(0);
assertFalse(mDatabase.inTransaction());
mDatabase.beginTransaction();
setNum(1);
assertTrue(mDatabase.inTransaction());
mDatabase.endTransaction();
assertFalse(mDatabase.inTransaction());
assertNum(0);
assertFalse(mDatabase.isDbLockedByCurrentThread());
assertFalse(mDatabase.isDbLockedByOtherThreads());
// it should throw IllegalStateException if we end a non-existent transaction.
assertThrowsIllegalState(new Runnable() {
public void run() {
mDatabase.endTransaction();
}
});
// it should throw IllegalStateException if a set a non-existent transaction as clean.
assertThrowsIllegalState(new Runnable() {
public void run() {
mDatabase.setTransactionSuccessful();
}
});
mDatabase.beginTransaction();
mDatabase.setTransactionSuccessful();
// it should throw IllegalStateException if we mark a transaction as clean twice.
assertThrowsIllegalState(new Runnable() {
public void run() {
mDatabase.setTransactionSuccessful();
}
});
// it should throw IllegalStateException if we begin a transaction after marking the
// parent as clean.
assertThrowsIllegalState(new Runnable() {
public void run() {
mDatabase.beginTransaction();
}
});
mDatabase.endTransaction();
assertFalse(mDatabase.isDbLockedByCurrentThread());
assertFalse(mDatabase.isDbLockedByOtherThreads());
assertFalse(mDatabase.inTransaction());
// Test a two-level transaction.
setNum(0);
mDatabase.beginTransaction();
assertTrue(mDatabase.inTransaction());
mDatabase.beginTransaction();
assertTrue(mDatabase.inTransaction());
setNum(1);
mDatabase.setTransactionSuccessful();
mDatabase.endTransaction();
assertTrue(mDatabase.inTransaction());
mDatabase.setTransactionSuccessful();
mDatabase.endTransaction();
assertFalse(mDatabase.inTransaction());
assertNum(1);
assertFalse(mDatabase.isDbLockedByCurrentThread());
assertFalse(mDatabase.isDbLockedByOtherThreads());
// Test rolling back an inner transaction.
setNum(0);
mDatabase.beginTransaction();
mDatabase.beginTransaction();
setNum(1);
mDatabase.endTransaction();
mDatabase.setTransactionSuccessful();
mDatabase.endTransaction();
assertNum(0);
assertFalse(mDatabase.isDbLockedByCurrentThread());
assertFalse(mDatabase.isDbLockedByOtherThreads());
// Test rolling back an outer transaction.
setNum(0);
mDatabase.beginTransaction();
mDatabase.beginTransaction();
setNum(1);
mDatabase.setTransactionSuccessful();
mDatabase.endTransaction();
mDatabase.endTransaction();
assertNum(0);
assertFalse(mDatabase.isDbLockedByCurrentThread());
assertFalse(mDatabase.isDbLockedByOtherThreads());
}
private void setNum(int num) {
mDatabase.execSQL("UPDATE test SET num = " + num);
}
private void assertNum(int num) {
assertEquals(num, DatabaseUtils.longForQuery(mDatabase,
"SELECT num FROM test", null));
}
private void assertThrowsIllegalState(Runnable r) {
try {
r.run();
fail("did not throw expected IllegalStateException");
} catch (IllegalStateException e) {
}
}
public void testAccessMaximumSize() {
long curMaximumSize = mDatabase.getMaximumSize();
// the new maximum size is less than the current size.
mDatabase.setMaximumSize(curMaximumSize - 1);
assertEquals(curMaximumSize, mDatabase.getMaximumSize());
// the new maximum size is more than the current size.
mDatabase.setMaximumSize(curMaximumSize + 1);
assertEquals(curMaximumSize + mDatabase.getPageSize(), mDatabase.getMaximumSize());
assertTrue(mDatabase.getMaximumSize() > curMaximumSize);
}
public void testAccessPageSize() {
File databaseFile = new File(mDatabaseDir, "database.db");
if (databaseFile.exists()) {
databaseFile.delete();
}
SQLiteDatabase database = null;
try {
database = SQLiteDatabase.openOrCreateDatabase(databaseFile.getPath(), null);
long initialValue = database.getPageSize();
// check that this does not throw an exception
// setting a different page size may not be supported after the DB has been created
database.setPageSize(initialValue);
assertEquals(initialValue, database.getPageSize());
} finally {
if (database != null) {
database.close();
databaseFile.delete();
}
}
}
public void testCompileStatement() {
mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, "
+ "name TEXT, age INTEGER, address TEXT);");
String name = "Mike";
int age = 21;
String address = "LA";
// at the beginning, there is no record in the database.
Cursor cursor = mDatabase.query("test", TEST_PROJECTION, null, null, null, null, null);
assertNotNull(cursor);
assertEquals(0, cursor.getCount());
String sql = "INSERT INTO test (name, age, address) VALUES (?, ?, ?);";
SQLiteStatement insertStatement = mDatabase.compileStatement(sql);
DatabaseUtils.bindObjectToProgram(insertStatement, 1, name);
DatabaseUtils.bindObjectToProgram(insertStatement, 2, age);
DatabaseUtils.bindObjectToProgram(insertStatement, 3, address);
insertStatement.execute();
insertStatement.close();
cursor.close();
cursor = mDatabase.query("test", TEST_PROJECTION, null, null, null, null, null);
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals(name, cursor.getString(COLUMN_NAME_INDEX));
assertEquals(age, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals(address, cursor.getString(COLUMN_ADDR_INDEX));
cursor.close();
SQLiteStatement deleteStatement = mDatabase.compileStatement("DELETE FROM test");
deleteStatement.execute();
cursor = mDatabase.query("test", null, null, null, null, null, null);
assertEquals(0, cursor.getCount());
cursor.deactivate();
deleteStatement.close();
cursor.close();
}
public void testDelete() {
mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, "
+ "name TEXT, age INTEGER, address TEXT);");
mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Mike', 20, 'LA');");
mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Jack', 30, 'London');");
mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Jim', 35, 'Chicago');");
// delete one record.
int count = mDatabase.delete(TABLE_NAME, "name = 'Mike'", null);
assertEquals(1, count);
Cursor cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null,
null, null, null, null);
assertNotNull(cursor);
// there are 2 records here.
assertEquals(2, cursor.getCount());
cursor.moveToFirst();
assertEquals("Jack", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(30, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals("London", cursor.getString(COLUMN_ADDR_INDEX));
cursor.moveToNext();
assertEquals("Jim", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(35, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals("Chicago", cursor.getString(COLUMN_ADDR_INDEX));
cursor.close();
// delete another record.
count = mDatabase.delete(TABLE_NAME, "name = ?", new String[] { "Jack" });
assertEquals(1, count);
cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null, null, null,
null, null);
assertNotNull(cursor);
// there are 1 records here.
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
assertEquals("Jim", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(35, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals("Chicago", cursor.getString(COLUMN_ADDR_INDEX));
cursor.close();
mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Mike', 20, 'LA');");
mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Jack', 30, 'London');");
// delete all records.
count = mDatabase.delete(TABLE_NAME, null, null);
assertEquals(3, count);
cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null, null, null, null, null);
assertNotNull(cursor);
assertEquals(0, cursor.getCount());
cursor.close();
}
public void testExecSQL() {
mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, "
+ "name TEXT, age INTEGER, address TEXT);");
// add a new record.
mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Mike', 20, 'LA');");
Cursor cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null,
null, null, null, null);
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
assertEquals("Mike", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(20, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals("LA", cursor.getString(COLUMN_ADDR_INDEX));
cursor.close();
// add other new record.
mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Jack', 30, 'London');");
cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null, null, null, null, null);
assertNotNull(cursor);
assertEquals(2, cursor.getCount());
cursor.moveToFirst();
assertEquals("Mike", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(20, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals("LA", cursor.getString(COLUMN_ADDR_INDEX));
cursor.moveToNext();
assertEquals("Jack", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(30, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals("London", cursor.getString(COLUMN_ADDR_INDEX));
cursor.close();
// delete a record.
mDatabase.execSQL("DELETE FROM test WHERE name = ?;", new String[] { "Jack" });
cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null, null, null, null, null);
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
assertEquals("Mike", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(20, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals("LA", cursor.getString(COLUMN_ADDR_INDEX));
cursor.close();
// delete a non-exist record.
mDatabase.execSQL("DELETE FROM test WHERE name = ?;", new String[] { "Wrong Name" });
cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null, null, null, null, null);
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
assertEquals("Mike", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(20, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals("LA", cursor.getString(COLUMN_ADDR_INDEX));
cursor.close();
try {
// execSQL can not use for query.
mDatabase.execSQL("SELECT * FROM test;");
fail("should throw SQLException.");
} catch (SQLException e) {
}
// make sure execSQL can't be used to execute more than 1 sql statement at a time
mDatabase.execSQL("UPDATE test SET age = 40 WHERE name = 'Mike';" +
"UPDATE test SET age = 50 WHERE name = 'Mike';");
// age should be updated to 40 not to 50
cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null, null, null, null, null);
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
assertEquals("Mike", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(40, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals("LA", cursor.getString(COLUMN_ADDR_INDEX));
cursor.close();
// make sure sql injection is NOT allowed or has no effect when using query()
String harmfulQuery = "name = 'Mike';UPDATE test SET age = 50 WHERE name = 'Mike'";
cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, harmfulQuery, null, null, null, null);
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
assertEquals("Mike", cursor.getString(COLUMN_NAME_INDEX));
// row's age column SHOULD NOT be 50
assertEquals(40, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals("LA", cursor.getString(COLUMN_ADDR_INDEX));
cursor.close();;
}
public void testFindEditTable() {
String tables = "table1 table2 table3";
assertEquals("table1", SQLiteDatabase.findEditTable(tables));
tables = "table1,table2,table3";
assertEquals("table1", SQLiteDatabase.findEditTable(tables));
tables = "table1";
assertEquals("table1", SQLiteDatabase.findEditTable(tables));
try {
SQLiteDatabase.findEditTable("");
fail("should throw IllegalStateException.");
} catch (IllegalStateException e) {
}
}
public void testGetPath() {
assertEquals(mDatabaseFilePath, mDatabase.getPath());
}
public void testAccessVersion() {
mDatabase.setVersion(1);
assertEquals(1, mDatabase.getVersion());
mDatabase.setVersion(3);
assertEquals(3, mDatabase.getVersion());
}
public void testInsert() {
mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, "
+ "name TEXT, age INTEGER, address TEXT);");
ContentValues values = new ContentValues();
values.put("name", "Jack");
values.put("age", 20);
values.put("address", "LA");
mDatabase.insert(TABLE_NAME, "name", values);
Cursor cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null,
null, null, null, null);
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
assertEquals("Jack", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(20, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals("LA", cursor.getString(COLUMN_ADDR_INDEX));
cursor.close();
mDatabase.insert(TABLE_NAME, "name", null);
cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null, null, null,
null, null);
assertNotNull(cursor);
assertEquals(2, cursor.getCount());
cursor.moveToFirst();
assertEquals("Jack", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(20, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals("LA", cursor.getString(COLUMN_ADDR_INDEX));
cursor.moveToNext();
assertNull(cursor.getString(COLUMN_NAME_INDEX));
cursor.close();
values = new ContentValues();
values.put("Wrong Key", "Wrong value");
mDatabase.insert(TABLE_NAME, "name", values);
// there are still 2 records.
cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null, null, null,
null, null);
assertNotNull(cursor);
assertEquals(2, cursor.getCount());
cursor.close();
// delete all record.
mDatabase.execSQL("DELETE FROM test;");
values = new ContentValues();
values.put("name", "Mike");
values.put("age", 30);
values.put("address", "London");
mDatabase.insertOrThrow(TABLE_NAME, "name", values);
cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null, null, null,
null, null);
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
assertEquals("Mike", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(30, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals("London", cursor.getString(COLUMN_ADDR_INDEX));
cursor.close();
mDatabase.insertOrThrow(TABLE_NAME, "name", null);
cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null, null, null,
null, null);
assertNotNull(cursor);
assertEquals(2, cursor.getCount());
cursor.moveToFirst();
assertEquals("Mike", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(30, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals("London", cursor.getString(COLUMN_ADDR_INDEX));
cursor.moveToNext();
assertNull(cursor.getString(COLUMN_NAME_INDEX));
cursor.close();
values = new ContentValues();
values.put("Wrong Key", "Wrong value");
try {
mDatabase.insertOrThrow(TABLE_NAME, "name", values);
fail("should throw SQLException.");
} catch (SQLException e) {
}
}
public void testIsOpen() {
assertTrue(mDatabase.isOpen());
mDatabase.close();
assertFalse(mDatabase.isOpen());
}
public void testIsReadOnly() {
assertFalse(mDatabase.isReadOnly());
SQLiteDatabase database = null;
try {
database = SQLiteDatabase.openDatabase(mDatabaseFilePath, null,
SQLiteDatabase.OPEN_READONLY);
assertTrue(database.isReadOnly());
} finally {
if (database != null) {
database.close();
}
}
}
public void testReleaseMemory() {
SQLiteDatabase.releaseMemory();
}
public void testSetLockingEnabled() {
mDatabase.execSQL("CREATE TABLE test (num INTEGER);");
mDatabase.execSQL("INSERT INTO test (num) VALUES (0)");
mDatabase.setLockingEnabled(false);
mDatabase.beginTransaction();
setNum(1);
assertNum(1);
mDatabase.setTransactionSuccessful();
mDatabase.endTransaction();
}
@SuppressWarnings("deprecation")
public void testYieldIfContended() {
assertFalse(mDatabase.yieldIfContended());
mDatabase.execSQL("CREATE TABLE test (num INTEGER);");
mDatabase.execSQL("INSERT INTO test (num) VALUES (0)");
// Make sure that things work outside an explicit transaction.
setNum(1);
assertNum(1);
setNum(0);
assertFalse(mDatabase.inTransaction());
mDatabase.beginTransaction();
assertTrue(mDatabase.inTransaction());
assertFalse(mDatabase.yieldIfContended());
setNum(1);
mDatabase.setTransactionSuccessful();
mDatabase.endTransaction();
mDatabase.beginTransaction();
assertTrue(mDatabase.inTransaction());
assertFalse(mDatabase.yieldIfContendedSafely());
setNum(1);
mDatabase.setTransactionSuccessful();
mDatabase.endTransaction();
}
public void testQuery() {
mDatabase.execSQL("CREATE TABLE employee (_id INTEGER PRIMARY KEY, " +
"name TEXT, month INTEGER, salary INTEGER);");
mDatabase.execSQL("INSERT INTO employee (name, month, salary) " +
"VALUES ('Mike', '1', '1000');");
mDatabase.execSQL("INSERT INTO employee (name, month, salary) " +
"VALUES ('Mike', '2', '3000');");
mDatabase.execSQL("INSERT INTO employee (name, month, salary) " +
"VALUES ('jack', '1', '2000');");
mDatabase.execSQL("INSERT INTO employee (name, month, salary) " +
"VALUES ('jack', '3', '1500');");
mDatabase.execSQL("INSERT INTO employee (name, month, salary) " +
"VALUES ('Jim', '1', '1000');");
mDatabase.execSQL("INSERT INTO employee (name, month, salary) " +
"VALUES ('Jim', '3', '3500');");
Cursor cursor = mDatabase.query(true, "employee", new String[] { "name", "sum(salary)" },
null, null, "name", "sum(salary)>1000", "name", null);
assertNotNull(cursor);
assertEquals(3, cursor.getCount());
final int COLUMN_NAME_INDEX = 0;
final int COLUMN_SALARY_INDEX = 1;
cursor.moveToFirst();
assertEquals("Jim", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(4500, cursor.getInt(COLUMN_SALARY_INDEX));
cursor.moveToNext();
assertEquals("Mike", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(4000, cursor.getInt(COLUMN_SALARY_INDEX));
cursor.moveToNext();
assertEquals("jack", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(3500, cursor.getInt(COLUMN_SALARY_INDEX));
cursor.close();
CursorFactory factory = new CursorFactory() {
public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
String editTable, SQLiteQuery query) {
return new MockSQLiteCursor(db, masterQuery, editTable, query);
}
};
cursor = mDatabase.queryWithFactory(factory, true, "employee",
new String[] { "name", "sum(salary)" },
null, null, "name", "sum(salary) > 1000", "name", null);
assertNotNull(cursor);
assertTrue(cursor instanceof MockSQLiteCursor);
cursor.moveToFirst();
assertEquals("Jim", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(4500, cursor.getInt(COLUMN_SALARY_INDEX));
cursor.moveToNext();
assertEquals("Mike", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(4000, cursor.getInt(COLUMN_SALARY_INDEX));
cursor.moveToNext();
assertEquals("jack", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(3500, cursor.getInt(COLUMN_SALARY_INDEX));
cursor.close();
cursor = mDatabase.query("employee", new String[] { "name", "sum(salary)" },
null, null, "name", "sum(salary) <= 4000", "name");
assertNotNull(cursor);
assertEquals(2, cursor.getCount());
cursor.moveToFirst();
assertEquals("Mike", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(4000, cursor.getInt(COLUMN_SALARY_INDEX));
cursor.moveToNext();
assertEquals("jack", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(3500, cursor.getInt(COLUMN_SALARY_INDEX));
cursor.close();
cursor = mDatabase.query("employee", new String[] { "name", "sum(salary)" },
null, null, "name", "sum(salary) > 1000", "name", "2");
assertNotNull(cursor);
assertEquals(2, cursor.getCount());
cursor.moveToFirst();
assertEquals("Jim", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(4500, cursor.getInt(COLUMN_SALARY_INDEX));
cursor.moveToNext();
assertEquals("Mike", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(4000, cursor.getInt(COLUMN_SALARY_INDEX));
cursor.close();
String sql = "SELECT name, month FROM employee WHERE salary > ?;";
cursor = mDatabase.rawQuery(sql, new String[] { "2000" });
assertNotNull(cursor);
assertEquals(2, cursor.getCount());
final int COLUMN_MONTH_INDEX = 1;
cursor.moveToFirst();
assertEquals("Mike", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(2, cursor.getInt(COLUMN_MONTH_INDEX));
cursor.moveToNext();
assertEquals("Jim", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(3, cursor.getInt(COLUMN_MONTH_INDEX));
cursor.close();
cursor = mDatabase.rawQueryWithFactory(factory, sql, new String[] { "2000" }, null);
assertNotNull(cursor);
assertEquals(2, cursor.getCount());
assertTrue(cursor instanceof MockSQLiteCursor);
cursor.moveToFirst();
assertEquals("Mike", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(2, cursor.getInt(COLUMN_MONTH_INDEX));
cursor.moveToNext();
assertEquals("Jim", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(3, cursor.getInt(COLUMN_MONTH_INDEX));
cursor.close();
}
public void testReplace() {
mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, "
+ "name TEXT, age INTEGER, address TEXT);");
ContentValues values = new ContentValues();
values.put("name", "Jack");
values.put("age", 20);
values.put("address", "LA");
mDatabase.replace(TABLE_NAME, "name", values);
Cursor cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION,
null, null, null, null, null);
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
int id = cursor.getInt(COLUMN_ID_INDEX);
assertEquals("Jack", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(20, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals("LA", cursor.getString(COLUMN_ADDR_INDEX));
cursor.close();
values = new ContentValues();
values.put("_id", id);
values.put("name", "Mike");
values.put("age", 40);
values.put("address", "London");
mDatabase.replace(TABLE_NAME, "name", values);
cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null, null, null, null, null);
assertNotNull(cursor);
assertEquals(1, cursor.getCount()); // there is still ONLY 1 record.
cursor.moveToFirst();
assertEquals("Mike", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(40, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals("London", cursor.getString(COLUMN_ADDR_INDEX));
cursor.close();
values = new ContentValues();
values.put("name", "Jack");
values.put("age", 20);
values.put("address", "LA");
mDatabase.replaceOrThrow(TABLE_NAME, "name", values);
cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null, null, null, null, null);
assertNotNull(cursor);
assertEquals(2, cursor.getCount());
cursor.moveToFirst();
assertEquals("Mike", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(40, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals("London", cursor.getString(COLUMN_ADDR_INDEX));
cursor.moveToNext();
assertEquals("Jack", cursor.getString(COLUMN_NAME_INDEX));
assertEquals(20, cursor.getInt(COLUMN_AGE_INDEX));
assertEquals("LA", cursor.getString(COLUMN_ADDR_INDEX));
cursor.close();
values = new ContentValues();
values.put("Wrong Key", "Wrong value");
try {
mDatabase.replaceOrThrow(TABLE_NAME, "name", values);
fail("should throw SQLException.");
} catch (SQLException e) {
}
}
public void testUpdate() {
mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");
mDatabase.execSQL("INSERT INTO test (data) VALUES ('string1');");
mDatabase.execSQL("INSERT INTO test (data) VALUES ('string2');");
mDatabase.execSQL("INSERT INTO test (data) VALUES ('string3');");
String updatedString = "this is an updated test";
ContentValues values = new ContentValues(1);
values.put("data", updatedString);
assertEquals(1, mDatabase.update("test", values, "_id=1", null));
Cursor cursor = mDatabase.query("test", null, "_id=1", null, null, null, null);
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
String value = cursor.getString(cursor.getColumnIndexOrThrow("data"));
assertEquals(updatedString, value);
cursor.close();
}
public void testNeedUpgrade() {
mDatabase.setVersion(0);
assertTrue(mDatabase.needUpgrade(1));
mDatabase.setVersion(1);
assertFalse(mDatabase.needUpgrade(1));
}
public void testSetLocale() {
final String[] STRINGS = {
"c\u00f4t\u00e9",
"cote",
"c\u00f4te",
"cot\u00e9",
"boy",
"dog",
"COTE",
};
mDatabase.execSQL("CREATE TABLE test (data TEXT COLLATE LOCALIZED);");
for (String s : STRINGS) {
mDatabase.execSQL("INSERT INTO test VALUES('" + s + "');");
}
mDatabase.setLocale(new Locale("en", "US"));
String sql = "SELECT data FROM test ORDER BY data COLLATE LOCALIZED ASC";
Cursor cursor = mDatabase.rawQuery(sql, null);
assertNotNull(cursor);
ArrayList<String> items = new ArrayList<String>();
while (cursor.moveToNext()) {
items.add(cursor.getString(0));
}
String[] results = items.toArray(new String[items.size()]);
assertEquals(STRINGS.length, results.length);
cursor.close();
// The database code currently uses PRIMARY collation strength,
// meaning that all versions of a character compare equal (regardless
// of case or accents), leaving the "cote" flavors in database order.
MoreAsserts.assertEquals(results, new String[] {
STRINGS[4], // "boy"
STRINGS[0], // sundry forms of "cote"
STRINGS[1],
STRINGS[2],
STRINGS[3],
STRINGS[6], // "COTE"
STRINGS[5], // "dog"
});
}
public void testOnAllReferencesReleased() {
assertTrue(mDatabase.isOpen());
mDatabase.releaseReference();
assertFalse(mDatabase.isOpen());
}
public void testTransactionWithSQLiteTransactionListener() {
mDatabase.execSQL("CREATE TABLE test (num INTEGER);");
mDatabase.execSQL("INSERT INTO test (num) VALUES (0)");
assertEquals(mTransactionListenerOnBeginCalled, false);
assertEquals(mTransactionListenerOnCommitCalled, false);
assertEquals(mTransactionListenerOnRollbackCalled, false);
mDatabase.beginTransactionWithListener(new TestSQLiteTransactionListener());
// Assert that the transcation has started
assertEquals(mTransactionListenerOnBeginCalled, true);
assertEquals(mTransactionListenerOnCommitCalled, false);
assertEquals(mTransactionListenerOnRollbackCalled, false);
setNum(1);
// State shouldn't have changed
assertEquals(mTransactionListenerOnBeginCalled, true);
assertEquals(mTransactionListenerOnCommitCalled, false);
assertEquals(mTransactionListenerOnRollbackCalled, false);
// commit the transaction
mDatabase.setTransactionSuccessful();
mDatabase.endTransaction();
// the listener should have been told that commit was called
assertEquals(mTransactionListenerOnBeginCalled, true);
assertEquals(mTransactionListenerOnCommitCalled, true);
assertEquals(mTransactionListenerOnRollbackCalled, false);
}
public void testRollbackTransactionWithSQLiteTransactionListener() {
mDatabase.execSQL("CREATE TABLE test (num INTEGER);");
mDatabase.execSQL("INSERT INTO test (num) VALUES (0)");
assertEquals(mTransactionListenerOnBeginCalled, false);
assertEquals(mTransactionListenerOnCommitCalled, false);
assertEquals(mTransactionListenerOnRollbackCalled, false);
mDatabase.beginTransactionWithListener(new TestSQLiteTransactionListener());
// Assert that the transcation has started
assertEquals(mTransactionListenerOnBeginCalled, true);
assertEquals(mTransactionListenerOnCommitCalled, false);
assertEquals(mTransactionListenerOnRollbackCalled, false);
setNum(1);
// State shouldn't have changed
assertEquals(mTransactionListenerOnBeginCalled, true);
assertEquals(mTransactionListenerOnCommitCalled, false);
assertEquals(mTransactionListenerOnRollbackCalled, false);
// commit the transaction
mDatabase.endTransaction();
// the listener should have been told that commit was called
assertEquals(mTransactionListenerOnBeginCalled, true);
assertEquals(mTransactionListenerOnCommitCalled, false);
assertEquals(mTransactionListenerOnRollbackCalled, true);
}
private class TestSQLiteTransactionListener implements SQLiteTransactionListener {
public void onBegin() {
mTransactionListenerOnBeginCalled = true;
}
public void onCommit() {
mTransactionListenerOnCommitCalled = true;
}
public void onRollback() {
mTransactionListenerOnRollbackCalled = true;
}
}
public void testGroupConcat() {
mDatabase.execSQL("CREATE TABLE test (i INT, j TEXT);");
// insert 2 rows
String sql = "INSERT INTO test (i) VALUES (?);";
SQLiteStatement insertStatement = mDatabase.compileStatement(sql);
DatabaseUtils.bindObjectToProgram(insertStatement, 1, 1);
insertStatement.execute();
DatabaseUtils.bindObjectToProgram(insertStatement, 1, 2);
insertStatement.execute();
insertStatement.close();
// make sure there are 2 rows in the table
Cursor cursor = mDatabase.rawQuery("SELECT count(*) FROM test", null);
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals(2, cursor.getInt(0));
cursor.close();
// concatenate column j from all the rows. should return NULL
cursor = mDatabase.rawQuery("SELECT group_concat(j, ' ') FROM test", null);
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertNull(cursor.getString(0));
cursor.close();
// drop the table
mDatabase.execSQL("DROP TABLE test;");
// should get no exceptions
}
public void testSchemaChanges() {
mDatabase.execSQL("CREATE TABLE test (i INT, j INT);");
// at the beginning, there is no record in the database.
Cursor cursor = mDatabase.rawQuery("SELECT * FROM test", null);
assertNotNull(cursor);
assertEquals(0, cursor.getCount());
cursor.close();
String sql = "INSERT INTO test VALUES (?, ?);";
SQLiteStatement insertStatement = mDatabase.compileStatement(sql);
DatabaseUtils.bindObjectToProgram(insertStatement, 1, 1);
DatabaseUtils.bindObjectToProgram(insertStatement, 2, 2);
insertStatement.execute();
insertStatement.close();
// read the data from the table and make sure it is correct
cursor = mDatabase.rawQuery("SELECT i,j FROM test", null);
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals(1, cursor.getInt(0));
assertEquals(2, cursor.getInt(1));
cursor.close();
// alter the table and execute another statement
mDatabase.execSQL("ALTER TABLE test ADD COLUMN k int;");
sql = "INSERT INTO test VALUES (?, ?, ?);";
insertStatement = mDatabase.compileStatement(sql);
DatabaseUtils.bindObjectToProgram(insertStatement, 1, 3);
DatabaseUtils.bindObjectToProgram(insertStatement, 2, 4);
DatabaseUtils.bindObjectToProgram(insertStatement, 3, 5);
insertStatement.execute();
insertStatement.close();
// read the data from the table and make sure it is correct
cursor = mDatabase.rawQuery("SELECT i,j,k FROM test", null);
assertNotNull(cursor);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals(1, cursor.getInt(0));
assertEquals(2, cursor.getInt(1));
assertNull(cursor.getString(2));
cursor.moveToNext();
assertEquals(3, cursor.getInt(0));
assertEquals(4, cursor.getInt(1));
assertEquals(5, cursor.getInt(2));
cursor.close();
// make sure the old statement - which should *try to reuse* cached query plan -
// still works
cursor = mDatabase.rawQuery("SELECT i,j FROM test", null);
assertNotNull(cursor);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals(1, cursor.getInt(0));
assertEquals(2, cursor.getInt(1));
cursor.moveToNext();
assertEquals(3, cursor.getInt(0));
assertEquals(4, cursor.getInt(1));
cursor.close();
SQLiteStatement deleteStatement = mDatabase.compileStatement("DELETE FROM test");
deleteStatement.execute();
deleteStatement.close();
}
public void testSchemaChangesNewTable() {
mDatabase.execSQL("CREATE TABLE test (i INT, j INT);");
// at the beginning, there is no record in the database.
Cursor cursor = mDatabase.rawQuery("SELECT * FROM test", null);
assertNotNull(cursor);
assertEquals(0, cursor.getCount());
cursor.close();
String sql = "INSERT INTO test VALUES (?, ?);";
SQLiteStatement insertStatement = mDatabase.compileStatement(sql);
DatabaseUtils.bindObjectToProgram(insertStatement, 1, 1);
DatabaseUtils.bindObjectToProgram(insertStatement, 2, 2);
insertStatement.execute();
insertStatement.close();
// read the data from the table and make sure it is correct
cursor = mDatabase.rawQuery("SELECT i,j FROM test", null);
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals(1, cursor.getInt(0));
assertEquals(2, cursor.getInt(1));
cursor.close();
// alter the table and execute another statement
mDatabase.execSQL("CREATE TABLE test_new (i INT, j INT, k INT);");
sql = "INSERT INTO test_new VALUES (?, ?, ?);";
insertStatement = mDatabase.compileStatement(sql);
DatabaseUtils.bindObjectToProgram(insertStatement, 1, 3);
DatabaseUtils.bindObjectToProgram(insertStatement, 2, 4);
DatabaseUtils.bindObjectToProgram(insertStatement, 3, 5);
insertStatement.execute();
insertStatement.close();
// read the data from the table and make sure it is correct
cursor = mDatabase.rawQuery("SELECT i,j,k FROM test_new", null);
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals(3, cursor.getInt(0));
assertEquals(4, cursor.getInt(1));
assertEquals(5, cursor.getInt(2));
cursor.close();
// make sure the old statement - which should *try to reuse* cached query plan -
// still works
cursor = mDatabase.rawQuery("SELECT i,j FROM test", null);
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals(1, cursor.getInt(0));
assertEquals(2, cursor.getInt(1));
cursor.close();
SQLiteStatement deleteStatement = mDatabase.compileStatement("DELETE FROM test");
deleteStatement.execute();
deleteStatement.close();
SQLiteStatement deleteStatement2 = mDatabase.compileStatement("DELETE FROM test_new");
deleteStatement2.execute();
deleteStatement2.close();
}
public void testSchemaChangesDropTable() {
mDatabase.execSQL("CREATE TABLE test (i INT, j INT);");
// at the beginning, there is no record in the database.
Cursor cursor = mDatabase.rawQuery("SELECT * FROM test", null);
assertNotNull(cursor);
assertEquals(0, cursor.getCount());
cursor.close();
String sql = "INSERT INTO test VALUES (?, ?);";
SQLiteStatement insertStatement = mDatabase.compileStatement(sql);
DatabaseUtils.bindObjectToProgram(insertStatement, 1, 1);
DatabaseUtils.bindObjectToProgram(insertStatement, 2, 2);
insertStatement.execute();
insertStatement.close();
// read the data from the table and make sure it is correct
cursor = mDatabase.rawQuery("SELECT i,j FROM test", null);
assertNotNull(cursor);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals(1, cursor.getInt(0));
assertEquals(2, cursor.getInt(1));
}
/**
* With sqlite's write-ahead-logging (WAL) enabled, readers get old version of data
* from the table that a writer is modifying at the same time.
* <p>
* This method does the following to test this sqlite3 feature
* <ol>
* <li>creates a table in the database and populates it with 5 rows of data</li>
* <li>do "select count(*) from this_table" and expect to receive 5</li>
* <li>start a writer thread who BEGINs a transaction, INSERTs a single row
* into this_table</li>
* <li>writer stops the transaction at this point, kicks off a reader thread - which will
* do the above SELECT query: "select count(*) from this_table"</li>
* <li>this query should return value 5 - because writer is still in transaction and
* sqlite returns OLD version of the data</li>
* <li>writer ends the transaction, thus making the extra row now visible to everyone</li>
* <li>reader is kicked off again to do the same query. this time query should
* return value = 6 which includes the newly inserted row into this_table.</li>
*</p>
* @throws InterruptedException
*/
@LargeTest
public void testReaderGetsOldVersionOfDataWhenWriterIsInXact() throws InterruptedException {
// redo setup to create WAL enabled database
mDatabase.close();
new File(mDatabase.getPath()).delete();
mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null, null);
boolean rslt = mDatabase.enableWriteAheadLogging();
assertTrue(rslt);
assertNotNull(mDatabase);
// create a new table and insert 5 records into it.
mDatabase.execSQL("CREATE TABLE t1 (i int, j int);");
mDatabase.beginTransaction();
for (int i = 0; i < 5; i++) {
mDatabase.execSQL("insert into t1 values(?,?);", new String[] {i+"", i+""});
}
mDatabase.setTransactionSuccessful();
mDatabase.endTransaction();
// make sure a reader can read the above data
ReaderQueryingData r1 = new ReaderQueryingData(5);
r1.start();
Thread.yield();
try {r1.join();} catch (Exception e) {}
WriterDoingSingleTransaction w = new WriterDoingSingleTransaction();
w.start();
w.join();
}
private class WriterDoingSingleTransaction extends Thread {
@Override public void run() {
// start a transaction
mDatabase.beginTransactionNonExclusive();
mDatabase.execSQL("insert into t1 values(?,?);", new String[] {"11", "11"});
assertTrue(mDatabase.isOpen());
// while the writer is in a transaction, start a reader and make sure it can still
// read 5 rows of data (= old data prior to the current transaction)
ReaderQueryingData r1 = new ReaderQueryingData(5);
r1.start();
try {r1.join();} catch (Exception e) {}
// now, have the writer do the select count(*)
// it should execute on the same connection as this transaction
// and count(*) should reflect the newly inserted row
Long l = DatabaseUtils.longForQuery(mDatabase, "select count(*) from t1", null);
assertEquals(6, l.intValue());
// end transaction
mDatabase.setTransactionSuccessful();
mDatabase.endTransaction();
// reader should now be able to read 6 rows = new data AFTER this transaction
r1 = new ReaderQueryingData(6);
r1.start();
try {r1.join();} catch (Exception e) {}
}
}
private class ReaderQueryingData extends Thread {
private int count;
/**
* constructor with a param to indicate the number of rows expected to be read
*/
public ReaderQueryingData(int count) {
this.count = count;
}
@Override public void run() {
Long l = DatabaseUtils.longForQuery(mDatabase, "select count(*) from t1", null);
assertEquals(count, l.intValue());
}
}
public void testExceptionsFromEnableWriteAheadLogging() {
// attach a database
// redo setup to create WAL enabled database
mDatabase.close();
new File(mDatabase.getPath()).delete();
mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null, null);
// attach a database and call enableWriteAheadLogging - should not be allowed
mDatabase.execSQL("attach database ':memory:' as memoryDb");
assertFalse(mDatabase.isWriteAheadLoggingEnabled());
assertFalse(mDatabase.enableWriteAheadLogging());
assertFalse(mDatabase.isWriteAheadLoggingEnabled());
// enableWriteAheadLogging on memory database is not allowed
SQLiteDatabase db = SQLiteDatabase.create(null);
assertFalse(mDatabase.isWriteAheadLoggingEnabled());
assertFalse(db.enableWriteAheadLogging());
assertFalse(mDatabase.isWriteAheadLoggingEnabled());
db.close();
}
public void testEnableThenDisableWriteAheadLogging() {
// Enable WAL.
assertFalse(mDatabase.isWriteAheadLoggingEnabled());
assertTrue(mDatabase.enableWriteAheadLogging());
assertTrue(mDatabase.isWriteAheadLoggingEnabled());
assertTrue(DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null)
.equalsIgnoreCase("WAL"));
// Enabling when already enabled should have no observable effect.
assertTrue(mDatabase.enableWriteAheadLogging());
assertTrue(mDatabase.isWriteAheadLoggingEnabled());
assertTrue(DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null)
.equalsIgnoreCase("WAL"));
// Disabling when there are no connections should work.
mDatabase.disableWriteAheadLogging();
assertFalse(mDatabase.isWriteAheadLoggingEnabled());
}
public void testEnableThenDisableWriteAheadLoggingUsingOpenFlag() {
new File(mDatabase.getPath()).delete();
mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile.getPath(), null,
SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING,
null);
assertTrue(mDatabase.isWriteAheadLoggingEnabled());
assertTrue(DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null)
.equalsIgnoreCase("WAL"));
// Enabling when already enabled should have no observable effect.
assertTrue(mDatabase.enableWriteAheadLogging());
assertTrue(mDatabase.isWriteAheadLoggingEnabled());
assertTrue(DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null)
.equalsIgnoreCase("WAL"));
// Disabling when there are no connections should work.
mDatabase.disableWriteAheadLogging();
assertFalse(mDatabase.isWriteAheadLoggingEnabled());
}
public void testEnableWriteAheadLoggingFromContextUsingModeFlag() {
// Without the MODE_ENABLE_WRITE_AHEAD_LOGGING flag, database opens without WAL.
getContext().deleteDatabase(DATABASE_FILE_NAME);
mDatabase = getContext().openOrCreateDatabase(DATABASE_FILE_NAME,
Context.MODE_PRIVATE, null);
assertFalse(mDatabase.isWriteAheadLoggingEnabled());
mDatabase.close();
// With the MODE_ENABLE_WRITE_AHEAD_LOGGING flag, database opens with WAL.
getContext().deleteDatabase(DATABASE_FILE_NAME);
mDatabase = getContext().openOrCreateDatabase(DATABASE_FILE_NAME,
Context.MODE_PRIVATE | Context.MODE_ENABLE_WRITE_AHEAD_LOGGING, null);
assertTrue(mDatabase.isWriteAheadLoggingEnabled());
mDatabase.close();
}
public void testEnableWriteAheadLoggingShouldThrowIfTransactionInProgress() {
assertFalse(mDatabase.isWriteAheadLoggingEnabled());
String oldJournalMode = DatabaseUtils.stringForQuery(
mDatabase, "PRAGMA journal_mode", null);
// Begin transaction.
mDatabase.beginTransaction();
try {
// Attempt to enable WAL should fail.
mDatabase.enableWriteAheadLogging();
fail("Expected IllegalStateException");
} catch (IllegalStateException ex) {
// expected
}
assertFalse(mDatabase.isWriteAheadLoggingEnabled());
assertTrue(DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null)
.equalsIgnoreCase(oldJournalMode));
}
public void testDisableWriteAheadLoggingShouldThrowIfTransactionInProgress() {
// Enable WAL.
assertFalse(mDatabase.isWriteAheadLoggingEnabled());
assertTrue(mDatabase.enableWriteAheadLogging());
assertTrue(mDatabase.isWriteAheadLoggingEnabled());
// Begin transaction.
mDatabase.beginTransaction();
try {
// Attempt to disable WAL should fail.
mDatabase.disableWriteAheadLogging();
fail("Expected IllegalStateException");
} catch (IllegalStateException ex) {
// expected
}
assertTrue(mDatabase.isWriteAheadLoggingEnabled());
assertTrue(DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null)
.equalsIgnoreCase("WAL"));
}
}