Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8eb1bfe0 authored by Vasu Nori's avatar Vasu Nori Committed by Android (Google) Code Review
Browse files

Merge "fix isIntegrityCheck() & add a sample impl class and test for DatabaseErrorHandler"

parents fddedbf9 bfe1dc27
Loading
Loading
Loading
Loading
+22 −16
Original line number Diff line number Diff line
@@ -39,7 +39,6 @@ import dalvik.system.BlockGuard;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -273,7 +272,7 @@ public class SQLiteDatabase extends SQLiteClosable {
     * invoked.
     *
     * this cache has an upper limit of mMaxSqlCacheSize (settable by calling the method
     * (@link setMaxSqlCacheSize(int)}).
     * (@link #setMaxSqlCacheSize(int)}).
     */
    // default statement-cache size per database connection ( = instance of this class)
    private int mMaxSqlCacheSize = 25;
@@ -903,8 +902,7 @@ public class SQLiteDatabase extends SQLiteClosable {
    public interface CursorFactory {
        /**
         * See
         * {@link SQLiteCursor#SQLiteCursor(SQLiteDatabase, SQLiteCursorDriver,
         * String, SQLiteQuery)}.
         * {@link SQLiteCursor#SQLiteCursor(SQLiteCursorDriver, String, SQLiteQuery)}.
         */
        public Cursor newCursor(SQLiteDatabase db,
                SQLiteCursorDriver masterQuery, String editTable,
@@ -2080,7 +2078,7 @@ public class SQLiteDatabase extends SQLiteClosable {
                 */
                if (++mCacheFullWarnings == MAX_WARNINGS_ON_CACHESIZE_CONDITION) {
                    Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
                            getPath() + ". Consider increasing cachesize.");
                            getPath() + ". Use setMaxSqlCacheSize() to increase cachesize. ");
                }
            } 
            /* add the given SQLiteCompiledSql compiledStatement to cache.
@@ -2142,7 +2140,7 @@ public class SQLiteDatabase extends SQLiteClosable {
     *
     * @param cacheSize the size of the cache. can be (0 to {@link #MAX_SQL_CACHE_SIZE})
     * @throws IllegalStateException if input cacheSize > {@link #MAX_SQL_CACHE_SIZE} or
     * > the value set with previous setMaxSqlCacheSize() call.
     * the value set with previous setMaxSqlCacheSize() call.
     */
    public synchronized void setMaxSqlCacheSize(int cacheSize) {
        if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
@@ -2342,10 +2340,12 @@ public class SQLiteDatabase extends SQLiteClosable {

    /* package */ SQLiteDatabase getDbConnection(String sql) {
        verifyDbIsOpen();
        // this method should always be called with main database connection handle
        // NEVER with pooled database connection handle
        // this method should always be called with main database connection handle.
        // the only time when it is called with pooled database connection handle is
        // corruption occurs while trying to open a pooled database connection handle.
        // in that case, simply return 'this' handle
        if (isPooledConnection()) {
            throw new IllegalStateException("incorrect database connection handle");
            return this;
        }

        // use the current connection handle if
@@ -2504,12 +2504,18 @@ public class SQLiteDatabase extends SQLiteClosable {
     */
    public boolean isDatabaseIntegrityOk() {
        verifyDbIsOpen();
        ArrayList<Pair<String, String>> attachedDbs = getAttachedDbs();
        ArrayList<Pair<String, String>> attachedDbs = null;
        try {
            attachedDbs = getAttachedDbs();
            if (attachedDbs == null) {
                throw new IllegalStateException("databaselist for: " + getPath() + " couldn't " +
                        "be retrieved. probably because the database is closed");
            }
        boolean isDatabaseCorrupt = false;
        } catch (SQLiteException e) {
            // can't get attachedDb list. do integrity check on the main database
            attachedDbs = new ArrayList<Pair<String, String>>();
            attachedDbs.add(new Pair<String, String>("main", this.mPath));
        }
        for (int i = 0; i < attachedDbs.size(); i++) {
            Pair<String, String> p = attachedDbs.get(i);
            SQLiteStatement prog = null;
@@ -2518,14 +2524,14 @@ public class SQLiteDatabase extends SQLiteClosable {
                String rslt = prog.simpleQueryForString();
                if (!rslt.equalsIgnoreCase("ok")) {
                    // integrity_checker failed on main or attached databases
                    isDatabaseCorrupt = true;
                    Log.e(TAG, "PRAGMA integrity_check on " + p.second + " returned: " + rslt);
                    return false;
                }
            } finally {
                if (prog != null) prog.close();
            }
        }
        return isDatabaseCorrupt;
        return true;
    }

    /**
+12 −0
Original line number Diff line number Diff line
@@ -16,6 +16,18 @@

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
 * from multiple threads, like so:
 *     thread # 1: in execute() method of the SQLiteStatement object
 *     while thread # 2: is in bind..() on the same object.
 *</p>
 * FIX this by NEVER sharing the same SQLiteStatement object between threads.
 * Create a local instance of the SQLiteStatement whenever it is needed, use it and close it ASAP.
 * NEVER make it globally available.
 */
public class SQLiteMisuseException extends SQLiteException {
    public SQLiteMisuseException() {}

+4 −4
Original line number Diff line number Diff line
@@ -205,7 +205,7 @@ static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow,
                    int offset = window->alloc(size);
                    if (!offset) {
                        window->freeLastRow();
                        LOGE("Failed allocating %u bytes for text/blob at %d,%d", size,
                        LOGD("Failed allocating %u bytes for text/blob at %d,%d", size,
                                   startPos + numRows, i);
                        return startPos + numRows + finish_program_and_get_row_count(statement) + 1;
                    }
@@ -225,7 +225,7 @@ static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow,
                    int64_t value = sqlite3_column_int64(statement, i);
                    if (!window->putLong(numRows, i, value)) {
                        window->freeLastRow();
                        LOGE("Failed allocating space for a long in column %d", i);
                        LOGD("Failed allocating space for a long in column %d", i);
                        return startPos + numRows + finish_program_and_get_row_count(statement) + 1;
                    }
                    LOG_WINDOW("%d,%d is INTEGER 0x%016llx", startPos + numRows, i, value);
@@ -234,7 +234,7 @@ static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow,
                    double value = sqlite3_column_double(statement, i);
                    if (!window->putDouble(numRows, i, value)) {
                        window->freeLastRow();
                        LOGE("Failed allocating space for a double in column %d", i);
                        LOGD("Failed allocating space for a double in column %d", i);
                        return startPos + numRows + finish_program_and_get_row_count(statement) + 1;
                    }
                    LOG_WINDOW("%d,%d is FLOAT %lf", startPos + numRows, i, value);
@@ -245,7 +245,7 @@ static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow,
                    int offset = window->alloc(size);
                    if (!offset) {
                        window->freeLastRow();
                        LOGE("Failed allocating %u bytes for blob at %d,%d", size,
                        LOGD("Failed allocating %u bytes for blob at %d,%d", size,
                                   startPos + numRows, i);
                        return startPos + numRows + finish_program_and_get_row_count(statement) + 1;
                    }
+111 −0
Original line number Diff line number Diff line
/*
 * 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;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.test.AndroidTestCase;
import android.util.Log;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class DatabaseErrorHandlerTest extends AndroidTestCase {

    private SQLiteDatabase mDatabase;
    private File mDatabaseFile;
    private static final String DB_NAME = "database_test.db";
    private File dbDir;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
        mDatabaseFile = new File(dbDir, DB_NAME);
        if (mDatabaseFile.exists()) {
            mDatabaseFile.delete();
        }
        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null,
                new MyDatabaseCorruptionHandler());
        assertNotNull(mDatabase);
    }

    @Override
    protected void tearDown() throws Exception {
        mDatabase.close();
        mDatabaseFile.delete();
        super.tearDown();
    }

    public void testNoCorruptionCase() {
        new MyDatabaseCorruptionHandler().onCorruption(mDatabase);
        // database file should still exist
        assertTrue(mDatabaseFile.exists());
    }

    public void testDatabaseIsCorrupt() throws IOException {
        mDatabase.execSQL("create table t (i int);");
        // write junk into the database file
        BufferedWriter writer = new BufferedWriter(new FileWriter(mDatabaseFile.getPath()));
        writer.write("blah");
        writer.close();
        assertTrue(mDatabaseFile.exists());
        // since the database file is now corrupt, doing any sql on this database connection
        // should trigger call to MyDatabaseCorruptionHandler.onCorruption
        try {
            mDatabase.execSQL("select * from t;");
            fail("expected exception");
        } catch (SQLiteException e) {
            // expected
        }
        // after corruption handler is called, the database file should be free of
        // database corruption
        SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null,
                new MyDatabaseCorruptionHandler());
        assertTrue(db.isDatabaseIntegrityOk());
    }

    /**
     * An example implementation of {@link DatabaseErrorHandler} to demonstrate
     * database corruption handler which checks to make sure database is indeed
     * corrupt before deleting the file.
     */
    public class MyDatabaseCorruptionHandler implements DatabaseErrorHandler {
        public void onCorruption(SQLiteDatabase dbObj) {
            boolean databaseOk = dbObj.isDatabaseIntegrityOk();
            // close the database
            try {
                dbObj.close();
            } catch (SQLiteException e) {
                /* ignore */
            }
            if (databaseOk) {
                // database is just fine. no need to delete the database file
                Log.e("MyDatabaseCorruptionHandler", "no corruption in the database: " +
                        mDatabaseFile.getPath());
            } else {
                // database is corrupt. delete the database file
                Log.e("MyDatabaseCorruptionHandler", "deleting the database file: " +
                        mDatabaseFile.getPath());
                new File(dbDir, DB_NAME).delete();
            }
        }
    }
}
 No newline at end of file