FileDocCategorySizeDatePackage
SQLiteConnection.javaAPI DocAndroid 5.1 API62943Thu Mar 12 22:22:10 GMT 2015android.database.sqlite

SQLiteConnection

public final class SQLiteConnection extends Object implements CancellationSignal.OnCancelListener
Represents a SQLite database connection. Each connection wraps an instance of a native sqlite3 object.

When database connection pooling is enabled, there can be multiple active connections to the same database. Otherwise there is typically only one connection per database.

When the SQLite WAL feature is enabled, multiple readers and one writer can concurrently access the database. Without WAL, readers and writers are mutually exclusive.

Ownership and concurrency guarantees

Connection objects are not thread-safe. They are acquired as needed to perform a database operation and are then returned to the pool. At any given time, a connection is either owned and used by a {@link SQLiteSession} object or the {@link SQLiteConnectionPool}. Those classes are responsible for serializing operations to guard against concurrent use of a connection.

The guarantee of having a single owner allows this class to be implemented without locks and greatly simplifies resource management.

Encapsulation guarantees

The connection object object owns *all* of the SQLite related native objects that are associated with the connection. What's more, there are no other objects in the system that are capable of obtaining handles to those native objects. Consequently, when the connection is closed, we do not have to worry about what other components might have references to its associated SQLite state -- there are none.

Encapsulation is what ensures that the connection object's lifecycle does not become a tortured mess of finalizers and reference queues.

Reentrance

This class must tolerate reentrant execution of SQLite operations because triggers may call custom SQLite functions that perform additional queries.

hide

Fields Summary
private static final String
TAG
private static final boolean
DEBUG
private static final String[]
EMPTY_STRING_ARRAY
private static final byte[]
EMPTY_BYTE_ARRAY
private static final Pattern
TRIM_SQL_PATTERN
private final dalvik.system.CloseGuard
mCloseGuard
private final SQLiteConnectionPool
mPool
private final SQLiteDatabaseConfiguration
mConfiguration
private final int
mConnectionId
private final boolean
mIsPrimaryConnection
private final boolean
mIsReadOnlyConnection
private final PreparedStatementCache
mPreparedStatementCache
private PreparedStatement
mPreparedStatementPool
private final OperationLog
mRecentOperations
private long
mConnectionPtr
private boolean
mOnlyAllowReadOnlyOperations
private int
mCancellationSignalAttachCount
Constructors Summary
private SQLiteConnection(SQLiteConnectionPool pool, SQLiteDatabaseConfiguration configuration, int connectionId, boolean primaryConnection)


             
               
         
         
             
           
           
           
           
           
           
           
             
           
             
           
               
           
               
           
               
           
               
        
               
           
           
           
        
               
           
        
               
        
                 
                 
         
         
           

      
             
                
        mPool = pool;
        mConfiguration = new SQLiteDatabaseConfiguration(configuration);
        mConnectionId = connectionId;
        mIsPrimaryConnection = primaryConnection;
        mIsReadOnlyConnection = (configuration.openFlags & SQLiteDatabase.OPEN_READONLY) != 0;
        mPreparedStatementCache = new PreparedStatementCache(
                mConfiguration.maxSqlCacheSize);
        mCloseGuard.open("close");
    
Methods Summary
private android.database.sqlite.SQLiteConnection$PreparedStatementacquirePreparedStatement(java.lang.String sql)

        PreparedStatement statement = mPreparedStatementCache.get(sql);
        boolean skipCache = false;
        if (statement != null) {
            if (!statement.mInUse) {
                return statement;
            }
            // The statement is already in the cache but is in use (this statement appears
            // to be not only re-entrant but recursive!).  So prepare a new copy of the
            // statement but do not cache it.
            skipCache = true;
        }

        final long statementPtr = nativePrepareStatement(mConnectionPtr, sql);
        try {
            final int numParameters = nativeGetParameterCount(mConnectionPtr, statementPtr);
            final int type = DatabaseUtils.getSqlStatementType(sql);
            final boolean readOnly = nativeIsReadOnly(mConnectionPtr, statementPtr);
            statement = obtainPreparedStatement(sql, statementPtr, numParameters, type, readOnly);
            if (!skipCache && isCacheable(type)) {
                mPreparedStatementCache.put(sql, statement);
                statement.mInCache = true;
            }
        } catch (RuntimeException ex) {
            // Finalize the statement if an exception occurred and we did not add
            // it to the cache.  If it is already in the cache, then leave it there.
            if (statement == null || !statement.mInCache) {
                nativeFinalizeStatement(mConnectionPtr, statementPtr);
            }
            throw ex;
        }
        statement.mInUse = true;
        return statement;
    
private voidapplyBlockGuardPolicy(android.database.sqlite.SQLiteConnection$PreparedStatement statement)

        if (!mConfiguration.isInMemoryDb()) {
            if (statement.mReadOnly) {
                BlockGuard.getThreadPolicy().onReadFromDisk();
            } else {
                BlockGuard.getThreadPolicy().onWriteToDisk();
            }
        }
    
private voidattachCancellationSignal(android.os.CancellationSignal cancellationSignal)

        if (cancellationSignal != null) {
            cancellationSignal.throwIfCanceled();

            mCancellationSignalAttachCount += 1;
            if (mCancellationSignalAttachCount == 1) {
                // Reset cancellation flag before executing the statement.
                nativeResetCancel(mConnectionPtr, true /*cancelable*/);

                // After this point, onCancel() may be called concurrently.
                cancellationSignal.setOnCancelListener(this);
            }
        }
    
private voidbindArguments(android.database.sqlite.SQLiteConnection$PreparedStatement statement, java.lang.Object[] bindArgs)

        final int count = bindArgs != null ? bindArgs.length : 0;
        if (count != statement.mNumParameters) {
            throw new SQLiteBindOrColumnIndexOutOfRangeException(
                    "Expected " + statement.mNumParameters + " bind arguments but "
                    + count + " were provided.");
        }
        if (count == 0) {
            return;
        }

        final long statementPtr = statement.mStatementPtr;
        for (int i = 0; i < count; i++) {
            final Object arg = bindArgs[i];
            switch (DatabaseUtils.getTypeOfObject(arg)) {
                case Cursor.FIELD_TYPE_NULL:
                    nativeBindNull(mConnectionPtr, statementPtr, i + 1);
                    break;
                case Cursor.FIELD_TYPE_INTEGER:
                    nativeBindLong(mConnectionPtr, statementPtr, i + 1,
                            ((Number)arg).longValue());
                    break;
                case Cursor.FIELD_TYPE_FLOAT:
                    nativeBindDouble(mConnectionPtr, statementPtr, i + 1,
                            ((Number)arg).doubleValue());
                    break;
                case Cursor.FIELD_TYPE_BLOB:
                    nativeBindBlob(mConnectionPtr, statementPtr, i + 1, (byte[])arg);
                    break;
                case Cursor.FIELD_TYPE_STRING:
                default:
                    if (arg instanceof Boolean) {
                        // Provide compatibility with legacy applications which may pass
                        // Boolean values in bind args.
                        nativeBindLong(mConnectionPtr, statementPtr, i + 1,
                                ((Boolean)arg).booleanValue() ? 1 : 0);
                    } else {
                        nativeBindString(mConnectionPtr, statementPtr, i + 1, arg.toString());
                    }
                    break;
            }
        }
    
private static java.lang.StringcanonicalizeSyncMode(java.lang.String value)

        if (value.equals("0")) {
            return "OFF";
        } else if (value.equals("1")) {
            return "NORMAL";
        } else if (value.equals("2")) {
            return "FULL";
        }
        return value;
    
voidclose()

        dispose(false);
    
voidcollectDbStats(java.util.ArrayList dbStatsList)
Collects statistics about database connection memory usage.

param
dbStatsList The list to populate.

        // Get information about the main database.
        int lookaside = nativeGetDbLookaside(mConnectionPtr);
        long pageCount = 0;
        long pageSize = 0;
        try {
            pageCount = executeForLong("PRAGMA page_count;", null, null);
            pageSize = executeForLong("PRAGMA page_size;", null, null);
        } catch (SQLiteException ex) {
            // Ignore.
        }
        dbStatsList.add(getMainDbStatsUnsafe(lookaside, pageCount, pageSize));

        // Get information about attached databases.
        // We ignore the first row in the database list because it corresponds to
        // the main database which we have already described.
        CursorWindow window = new CursorWindow("collectDbStats");
        try {
            executeForCursorWindow("PRAGMA database_list;", null, window, 0, 0, false, null);
            for (int i = 1; i < window.getNumRows(); i++) {
                String name = window.getString(i, 1);
                String path = window.getString(i, 2);
                pageCount = 0;
                pageSize = 0;
                try {
                    pageCount = executeForLong("PRAGMA " + name + ".page_count;", null, null);
                    pageSize = executeForLong("PRAGMA " + name + ".page_size;", null, null);
                } catch (SQLiteException ex) {
                    // Ignore.
                }
                String label = "  (attached) " + name;
                if (!path.isEmpty()) {
                    label += ": " + path;
                }
                dbStatsList.add(new DbStats(label, pageCount, pageSize, 0, 0, 0, 0));
            }
        } catch (SQLiteException ex) {
            // Ignore.
        } finally {
            window.close();
        }
    
voidcollectDbStatsUnsafe(java.util.ArrayList dbStatsList)
Collects statistics about database connection memory usage, in the case where the caller might not actually own the connection.

return
The statistics object, never null.

        dbStatsList.add(getMainDbStatsUnsafe(0, 0, 0));
    
java.lang.StringdescribeCurrentOperationUnsafe()
Describes the currently executing operation, in the case where the caller might not actually own the connection. This function is written so that it may be called by a thread that does not own the connection. We need to be very careful because the connection state is not synchronized. At worst, the method may return stale or slightly wrong data, however it should not crash. This is ok as it is only used for diagnostic purposes.

return
A description of the current operation including how long it has been running, or null if none.

        return mRecentOperations.describeCurrentOperation();
    
private voiddetachCancellationSignal(android.os.CancellationSignal cancellationSignal)

        if (cancellationSignal != null) {
            assert mCancellationSignalAttachCount > 0;

            mCancellationSignalAttachCount -= 1;
            if (mCancellationSignalAttachCount == 0) {
                // After this point, onCancel() cannot be called concurrently.
                cancellationSignal.setOnCancelListener(null);

                // Reset cancellation flag after executing the statement.
                nativeResetCancel(mConnectionPtr, false /*cancelable*/);
            }
        }
    
private voiddispose(boolean finalized)

        if (mCloseGuard != null) {
            if (finalized) {
                mCloseGuard.warnIfOpen();
            }
            mCloseGuard.close();
        }

        if (mConnectionPtr != 0) {
            final int cookie = mRecentOperations.beginOperation("close", null, null);
            try {
                mPreparedStatementCache.evictAll();
                nativeClose(mConnectionPtr);
                mConnectionPtr = 0;
            } finally {
                mRecentOperations.endOperation(cookie);
            }
        }
    
public voiddump(android.util.Printer printer, boolean verbose)
Dumps debugging information about this connection.

param
printer The printer to receive the dump, not null.
param
verbose True to dump more verbose information.

        dumpUnsafe(printer, verbose);
    
voiddumpUnsafe(android.util.Printer printer, boolean verbose)
Dumps debugging information about this connection, in the case where the caller might not actually own the connection. This function is written so that it may be called by a thread that does not own the connection. We need to be very careful because the connection state is not synchronized. At worst, the method may return stale or slightly wrong data, however it should not crash. This is ok as it is only used for diagnostic purposes.

param
printer The printer to receive the dump, not null.
param
verbose True to dump more verbose information.

        printer.println("Connection #" + mConnectionId + ":");
        if (verbose) {
            printer.println("  connectionPtr: 0x" + Long.toHexString(mConnectionPtr));
        }
        printer.println("  isPrimaryConnection: " + mIsPrimaryConnection);
        printer.println("  onlyAllowReadOnlyOperations: " + mOnlyAllowReadOnlyOperations);

        mRecentOperations.dump(printer, verbose);

        if (verbose) {
            mPreparedStatementCache.dump(printer);
        }
    
public voidexecute(java.lang.String sql, java.lang.Object[] bindArgs, android.os.CancellationSignal cancellationSignal)
Executes a statement that does not return a result.

param
sql The SQL statement to execute.
param
bindArgs The arguments to bind, or null if none.
param
cancellationSignal A signal to cancel the operation in progress, or null if none.
throws
SQLiteException if an error occurs, such as a syntax error or invalid number of bind arguments.
throws
OperationCanceledException if the operation was canceled.

        if (sql == null) {
            throw new IllegalArgumentException("sql must not be null.");
        }

        final int cookie = mRecentOperations.beginOperation("execute", sql, bindArgs);
        try {
            final PreparedStatement statement = acquirePreparedStatement(sql);
            try {
                throwIfStatementForbidden(statement);
                bindArguments(statement, bindArgs);
                applyBlockGuardPolicy(statement);
                attachCancellationSignal(cancellationSignal);
                try {
                    nativeExecute(mConnectionPtr, statement.mStatementPtr);
                } finally {
                    detachCancellationSignal(cancellationSignal);
                }
            } finally {
                releasePreparedStatement(statement);
            }
        } catch (RuntimeException ex) {
            mRecentOperations.failOperation(cookie, ex);
            throw ex;
        } finally {
            mRecentOperations.endOperation(cookie);
        }
    
public android.os.ParcelFileDescriptorexecuteForBlobFileDescriptor(java.lang.String sql, java.lang.Object[] bindArgs, android.os.CancellationSignal cancellationSignal)
Executes a statement that returns a single BLOB result as a file descriptor to a shared memory region.

param
sql The SQL statement to execute.
param
bindArgs The arguments to bind, or null if none.
param
cancellationSignal A signal to cancel the operation in progress, or null if none.
return
The file descriptor for a shared memory region that contains the value of the first column in the first row of the result set as a BLOB, or null if none.
throws
SQLiteException if an error occurs, such as a syntax error or invalid number of bind arguments.
throws
OperationCanceledException if the operation was canceled.

        if (sql == null) {
            throw new IllegalArgumentException("sql must not be null.");
        }

        final int cookie = mRecentOperations.beginOperation("executeForBlobFileDescriptor",
                sql, bindArgs);
        try {
            final PreparedStatement statement = acquirePreparedStatement(sql);
            try {
                throwIfStatementForbidden(statement);
                bindArguments(statement, bindArgs);
                applyBlockGuardPolicy(statement);
                attachCancellationSignal(cancellationSignal);
                try {
                    int fd = nativeExecuteForBlobFileDescriptor(
                            mConnectionPtr, statement.mStatementPtr);
                    return fd >= 0 ? ParcelFileDescriptor.adoptFd(fd) : null;
                } finally {
                    detachCancellationSignal(cancellationSignal);
                }
            } finally {
                releasePreparedStatement(statement);
            }
        } catch (RuntimeException ex) {
            mRecentOperations.failOperation(cookie, ex);
            throw ex;
        } finally {
            mRecentOperations.endOperation(cookie);
        }
    
public intexecuteForChangedRowCount(java.lang.String sql, java.lang.Object[] bindArgs, android.os.CancellationSignal cancellationSignal)
Executes a statement that returns a count of the number of rows that were changed. Use for UPDATE or DELETE SQL statements.

param
sql The SQL statement to execute.
param
bindArgs The arguments to bind, or null if none.
param
cancellationSignal A signal to cancel the operation in progress, or null if none.
return
The number of rows that were changed.
throws
SQLiteException if an error occurs, such as a syntax error or invalid number of bind arguments.
throws
OperationCanceledException if the operation was canceled.

        if (sql == null) {
            throw new IllegalArgumentException("sql must not be null.");
        }

        int changedRows = 0;
        final int cookie = mRecentOperations.beginOperation("executeForChangedRowCount",
                sql, bindArgs);
        try {
            final PreparedStatement statement = acquirePreparedStatement(sql);
            try {
                throwIfStatementForbidden(statement);
                bindArguments(statement, bindArgs);
                applyBlockGuardPolicy(statement);
                attachCancellationSignal(cancellationSignal);
                try {
                    changedRows = nativeExecuteForChangedRowCount(
                            mConnectionPtr, statement.mStatementPtr);
                    return changedRows;
                } finally {
                    detachCancellationSignal(cancellationSignal);
                }
            } finally {
                releasePreparedStatement(statement);
            }
        } catch (RuntimeException ex) {
            mRecentOperations.failOperation(cookie, ex);
            throw ex;
        } finally {
            if (mRecentOperations.endOperationDeferLog(cookie)) {
                mRecentOperations.logOperation(cookie, "changedRows=" + changedRows);
            }
        }
    
public intexecuteForCursorWindow(java.lang.String sql, java.lang.Object[] bindArgs, android.database.CursorWindow window, int startPos, int requiredPos, boolean countAllRows, android.os.CancellationSignal cancellationSignal)
Executes a statement and populates the specified {@link CursorWindow} with a range of results. Returns the number of rows that were counted during query execution.

param
sql The SQL statement to execute.
param
bindArgs The arguments to bind, or null if none.
param
window The cursor window to clear and fill.
param
startPos The start position for filling the window.
param
requiredPos The position of a row that MUST be in the window. If it won't fit, then the query should discard part of what it filled so that it does. Must be greater than or equal to startPos.
param
countAllRows True to count all rows that the query would return regagless of whether they fit in the window.
param
cancellationSignal A signal to cancel the operation in progress, or null if none.
return
The number of rows that were counted during query execution. Might not be all rows in the result set unless countAllRows is true.
throws
SQLiteException if an error occurs, such as a syntax error or invalid number of bind arguments.
throws
OperationCanceledException if the operation was canceled.

        if (sql == null) {
            throw new IllegalArgumentException("sql must not be null.");
        }
        if (window == null) {
            throw new IllegalArgumentException("window must not be null.");
        }

        window.acquireReference();
        try {
            int actualPos = -1;
            int countedRows = -1;
            int filledRows = -1;
            final int cookie = mRecentOperations.beginOperation("executeForCursorWindow",
                    sql, bindArgs);
            try {
                final PreparedStatement statement = acquirePreparedStatement(sql);
                try {
                    throwIfStatementForbidden(statement);
                    bindArguments(statement, bindArgs);
                    applyBlockGuardPolicy(statement);
                    attachCancellationSignal(cancellationSignal);
                    try {
                        final long result = nativeExecuteForCursorWindow(
                                mConnectionPtr, statement.mStatementPtr, window.mWindowPtr,
                                startPos, requiredPos, countAllRows);
                        actualPos = (int)(result >> 32);
                        countedRows = (int)result;
                        filledRows = window.getNumRows();
                        window.setStartPosition(actualPos);
                        return countedRows;
                    } finally {
                        detachCancellationSignal(cancellationSignal);
                    }
                } finally {
                    releasePreparedStatement(statement);
                }
            } catch (RuntimeException ex) {
                mRecentOperations.failOperation(cookie, ex);
                throw ex;
            } finally {
                if (mRecentOperations.endOperationDeferLog(cookie)) {
                    mRecentOperations.logOperation(cookie, "window='" + window
                            + "', startPos=" + startPos
                            + ", actualPos=" + actualPos
                            + ", filledRows=" + filledRows
                            + ", countedRows=" + countedRows);
                }
            }
        } finally {
            window.releaseReference();
        }
    
public longexecuteForLastInsertedRowId(java.lang.String sql, java.lang.Object[] bindArgs, android.os.CancellationSignal cancellationSignal)
Executes a statement that returns the row id of the last row inserted by the statement. Use for INSERT SQL statements.

param
sql The SQL statement to execute.
param
bindArgs The arguments to bind, or null if none.
param
cancellationSignal A signal to cancel the operation in progress, or null if none.
return
The row id of the last row that was inserted, or 0 if none.
throws
SQLiteException if an error occurs, such as a syntax error or invalid number of bind arguments.
throws
OperationCanceledException if the operation was canceled.

        if (sql == null) {
            throw new IllegalArgumentException("sql must not be null.");
        }

        final int cookie = mRecentOperations.beginOperation("executeForLastInsertedRowId",
                sql, bindArgs);
        try {
            final PreparedStatement statement = acquirePreparedStatement(sql);
            try {
                throwIfStatementForbidden(statement);
                bindArguments(statement, bindArgs);
                applyBlockGuardPolicy(statement);
                attachCancellationSignal(cancellationSignal);
                try {
                    return nativeExecuteForLastInsertedRowId(
                            mConnectionPtr, statement.mStatementPtr);
                } finally {
                    detachCancellationSignal(cancellationSignal);
                }
            } finally {
                releasePreparedStatement(statement);
            }
        } catch (RuntimeException ex) {
            mRecentOperations.failOperation(cookie, ex);
            throw ex;
        } finally {
            mRecentOperations.endOperation(cookie);
        }
    
public longexecuteForLong(java.lang.String sql, java.lang.Object[] bindArgs, android.os.CancellationSignal cancellationSignal)
Executes a statement that returns a single long result.

param
sql The SQL statement to execute.
param
bindArgs The arguments to bind, or null if none.
param
cancellationSignal A signal to cancel the operation in progress, or null if none.
return
The value of the first column in the first row of the result set as a long, or zero if none.
throws
SQLiteException if an error occurs, such as a syntax error or invalid number of bind arguments.
throws
OperationCanceledException if the operation was canceled.

        if (sql == null) {
            throw new IllegalArgumentException("sql must not be null.");
        }

        final int cookie = mRecentOperations.beginOperation("executeForLong", sql, bindArgs);
        try {
            final PreparedStatement statement = acquirePreparedStatement(sql);
            try {
                throwIfStatementForbidden(statement);
                bindArguments(statement, bindArgs);
                applyBlockGuardPolicy(statement);
                attachCancellationSignal(cancellationSignal);
                try {
                    return nativeExecuteForLong(mConnectionPtr, statement.mStatementPtr);
                } finally {
                    detachCancellationSignal(cancellationSignal);
                }
            } finally {
                releasePreparedStatement(statement);
            }
        } catch (RuntimeException ex) {
            mRecentOperations.failOperation(cookie, ex);
            throw ex;
        } finally {
            mRecentOperations.endOperation(cookie);
        }
    
public java.lang.StringexecuteForString(java.lang.String sql, java.lang.Object[] bindArgs, android.os.CancellationSignal cancellationSignal)
Executes a statement that returns a single {@link String} result.

param
sql The SQL statement to execute.
param
bindArgs The arguments to bind, or null if none.
param
cancellationSignal A signal to cancel the operation in progress, or null if none.
return
The value of the first column in the first row of the result set as a String, or null if none.
throws
SQLiteException if an error occurs, such as a syntax error or invalid number of bind arguments.
throws
OperationCanceledException if the operation was canceled.

        if (sql == null) {
            throw new IllegalArgumentException("sql must not be null.");
        }

        final int cookie = mRecentOperations.beginOperation("executeForString", sql, bindArgs);
        try {
            final PreparedStatement statement = acquirePreparedStatement(sql);
            try {
                throwIfStatementForbidden(statement);
                bindArguments(statement, bindArgs);
                applyBlockGuardPolicy(statement);
                attachCancellationSignal(cancellationSignal);
                try {
                    return nativeExecuteForString(mConnectionPtr, statement.mStatementPtr);
                } finally {
                    detachCancellationSignal(cancellationSignal);
                }
            } finally {
                releasePreparedStatement(statement);
            }
        } catch (RuntimeException ex) {
            mRecentOperations.failOperation(cookie, ex);
            throw ex;
        } finally {
            mRecentOperations.endOperation(cookie);
        }
    
protected voidfinalize()

        try {
            if (mPool != null && mConnectionPtr != 0) {
                mPool.onConnectionLeaked();
            }

            dispose(true);
        } finally {
            super.finalize();
        }
    
private voidfinalizePreparedStatement(android.database.sqlite.SQLiteConnection$PreparedStatement statement)

        nativeFinalizeStatement(mConnectionPtr, statement.mStatementPtr);
        recyclePreparedStatement(statement);
    
public intgetConnectionId()
Gets the unique id of this connection.

return
The connection id.

        return mConnectionId;
    
private android.database.sqlite.SQLiteDebug.DbStatsgetMainDbStatsUnsafe(int lookaside, long pageCount, long pageSize)

        // The prepared statement cache is thread-safe so we can access its statistics
        // even if we do not own the database connection.
        String label = mConfiguration.path;
        if (!mIsPrimaryConnection) {
            label += " (" + mConnectionId + ")";
        }
        return new DbStats(label, pageCount, pageSize, lookaside,
                mPreparedStatementCache.hitCount(),
                mPreparedStatementCache.missCount(),
                mPreparedStatementCache.size());
    
private static booleanisCacheable(int statementType)

        if (statementType == DatabaseUtils.STATEMENT_UPDATE
                || statementType == DatabaseUtils.STATEMENT_SELECT) {
            return true;
        }
        return false;
    
booleanisPreparedStatementInCache(java.lang.String sql)

        return mPreparedStatementCache.get(sql) != null;
    
public booleanisPrimaryConnection()
Returns true if this is the primary database connection.

return
True if this is the primary database connection.

        return mIsPrimaryConnection;
    
private static native voidnativeBindBlob(long connectionPtr, long statementPtr, int index, byte[] value)

private static native voidnativeBindDouble(long connectionPtr, long statementPtr, int index, double value)

private static native voidnativeBindLong(long connectionPtr, long statementPtr, int index, long value)

private static native voidnativeBindNull(long connectionPtr, long statementPtr, int index)

private static native voidnativeBindString(long connectionPtr, long statementPtr, int index, java.lang.String value)

private static native voidnativeCancel(long connectionPtr)

private static native voidnativeClose(long connectionPtr)

private static native voidnativeExecute(long connectionPtr, long statementPtr)

private static native intnativeExecuteForBlobFileDescriptor(long connectionPtr, long statementPtr)

private static native intnativeExecuteForChangedRowCount(long connectionPtr, long statementPtr)

private static native longnativeExecuteForCursorWindow(long connectionPtr, long statementPtr, long windowPtr, int startPos, int requiredPos, boolean countAllRows)

private static native longnativeExecuteForLastInsertedRowId(long connectionPtr, long statementPtr)

private static native longnativeExecuteForLong(long connectionPtr, long statementPtr)

private static native java.lang.StringnativeExecuteForString(long connectionPtr, long statementPtr)

private static native voidnativeFinalizeStatement(long connectionPtr, long statementPtr)

private static native intnativeGetColumnCount(long connectionPtr, long statementPtr)

private static native java.lang.StringnativeGetColumnName(long connectionPtr, long statementPtr, int index)

private static native intnativeGetDbLookaside(long connectionPtr)

private static native intnativeGetParameterCount(long connectionPtr, long statementPtr)

private static native booleannativeIsReadOnly(long connectionPtr, long statementPtr)

private static native longnativeOpen(java.lang.String path, int openFlags, java.lang.String label, boolean enableTrace, boolean enableProfile)

private static native longnativePrepareStatement(long connectionPtr, java.lang.String sql)

private static native voidnativeRegisterCustomFunction(long connectionPtr, SQLiteCustomFunction function)

private static native voidnativeRegisterLocalizedCollators(long connectionPtr, java.lang.String locale)

private static native voidnativeResetCancel(long connectionPtr, boolean cancelable)

private static native voidnativeResetStatementAndClearBindings(long connectionPtr, long statementPtr)

private android.database.sqlite.SQLiteConnection$PreparedStatementobtainPreparedStatement(java.lang.String sql, long statementPtr, int numParameters, int type, boolean readOnly)

        PreparedStatement statement = mPreparedStatementPool;
        if (statement != null) {
            mPreparedStatementPool = statement.mPoolNext;
            statement.mPoolNext = null;
            statement.mInCache = false;
        } else {
            statement = new PreparedStatement();
        }
        statement.mSql = sql;
        statement.mStatementPtr = statementPtr;
        statement.mNumParameters = numParameters;
        statement.mType = type;
        statement.mReadOnly = readOnly;
        return statement;
    
public voidonCancel()

        nativeCancel(mConnectionPtr);
    
static android.database.sqlite.SQLiteConnectionopen(SQLiteConnectionPool pool, SQLiteDatabaseConfiguration configuration, int connectionId, boolean primaryConnection)

        SQLiteConnection connection = new SQLiteConnection(pool, configuration,
                connectionId, primaryConnection);
        try {
            connection.open();
            return connection;
        } catch (SQLiteException ex) {
            connection.dispose(false);
            throw ex;
        }
    
private voidopen()

        mConnectionPtr = nativeOpen(mConfiguration.path, mConfiguration.openFlags,
                mConfiguration.label,
                SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME);

        setPageSize();
        setForeignKeyModeFromConfiguration();
        setWalModeFromConfiguration();
        setJournalSizeLimit();
        setAutoCheckpointInterval();
        setLocaleFromConfiguration();

        // Register custom functions.
        final int functionCount = mConfiguration.customFunctions.size();
        for (int i = 0; i < functionCount; i++) {
            SQLiteCustomFunction function = mConfiguration.customFunctions.get(i);
            nativeRegisterCustomFunction(mConnectionPtr, function);
        }
    
public voidprepare(java.lang.String sql, SQLiteStatementInfo outStatementInfo)
Prepares a statement for execution but does not bind its parameters or execute it.

This method can be used to check for syntax errors during compilation prior to execution of the statement. If the {@code outStatementInfo} argument is not null, the provided {@link SQLiteStatementInfo} object is populated with information about the statement.

A prepared statement makes no reference to the arguments that may eventually be bound to it, consequently it it possible to cache certain prepared statements such as SELECT or INSERT/UPDATE statements. If the statement is cacheable, then it will be stored in the cache for later.

To take advantage of this behavior as an optimization, the connection pool provides a method to acquire a connection that already has a given SQL statement in its prepared statement cache so that it is ready for execution.

param
sql The SQL statement to prepare.
param
outStatementInfo The {@link SQLiteStatementInfo} object to populate with information about the statement, or null if none.
throws
SQLiteException if an error occurs, such as a syntax error.

        if (sql == null) {
            throw new IllegalArgumentException("sql must not be null.");
        }

        final int cookie = mRecentOperations.beginOperation("prepare", sql, null);
        try {
            final PreparedStatement statement = acquirePreparedStatement(sql);
            try {
                if (outStatementInfo != null) {
                    outStatementInfo.numParameters = statement.mNumParameters;
                    outStatementInfo.readOnly = statement.mReadOnly;

                    final int columnCount = nativeGetColumnCount(
                            mConnectionPtr, statement.mStatementPtr);
                    if (columnCount == 0) {
                        outStatementInfo.columnNames = EMPTY_STRING_ARRAY;
                    } else {
                        outStatementInfo.columnNames = new String[columnCount];
                        for (int i = 0; i < columnCount; i++) {
                            outStatementInfo.columnNames[i] = nativeGetColumnName(
                                    mConnectionPtr, statement.mStatementPtr, i);
                        }
                    }
                }
            } finally {
                releasePreparedStatement(statement);
            }
        } catch (RuntimeException ex) {
            mRecentOperations.failOperation(cookie, ex);
            throw ex;
        } finally {
            mRecentOperations.endOperation(cookie);
        }
    
voidreconfigure(SQLiteDatabaseConfiguration configuration)

        mOnlyAllowReadOnlyOperations = false;

        // Register custom functions.
        final int functionCount = configuration.customFunctions.size();
        for (int i = 0; i < functionCount; i++) {
            SQLiteCustomFunction function = configuration.customFunctions.get(i);
            if (!mConfiguration.customFunctions.contains(function)) {
                nativeRegisterCustomFunction(mConnectionPtr, function);
            }
        }

        // Remember what changed.
        boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled
                != mConfiguration.foreignKeyConstraintsEnabled;
        boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
                & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
        boolean localeChanged = !configuration.locale.equals(mConfiguration.locale);

        // Update configuration parameters.
        mConfiguration.updateParametersFrom(configuration);

        // Update prepared statement cache size.
        mPreparedStatementCache.resize(configuration.maxSqlCacheSize);

        // Update foreign key mode.
        if (foreignKeyModeChanged) {
            setForeignKeyModeFromConfiguration();
        }

        // Update WAL.
        if (walModeChanged) {
            setWalModeFromConfiguration();
        }

        // Update locale.
        if (localeChanged) {
            setLocaleFromConfiguration();
        }
    
private voidrecyclePreparedStatement(android.database.sqlite.SQLiteConnection$PreparedStatement statement)

        statement.mSql = null;
        statement.mPoolNext = mPreparedStatementPool;
        mPreparedStatementPool = statement;
    
private voidreleasePreparedStatement(android.database.sqlite.SQLiteConnection$PreparedStatement statement)

        statement.mInUse = false;
        if (statement.mInCache) {
            try {
                nativeResetStatementAndClearBindings(mConnectionPtr, statement.mStatementPtr);
            } catch (SQLiteException ex) {
                // The statement could not be reset due to an error.  Remove it from the cache.
                // When remove() is called, the cache will invoke its entryRemoved() callback,
                // which will in turn call finalizePreparedStatement() to finalize and
                // recycle the statement.
                if (DEBUG) {
                    Log.d(TAG, "Could not reset prepared statement due to an exception.  "
                            + "Removing it from the cache.  SQL: "
                            + trimSqlForDisplay(statement.mSql), ex);
                }

                mPreparedStatementCache.remove(statement.mSql);
            }
        } else {
            finalizePreparedStatement(statement);
        }
    
private voidsetAutoCheckpointInterval()

        if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
            final long newValue = SQLiteGlobal.getWALAutoCheckpoint();
            long value = executeForLong("PRAGMA wal_autocheckpoint", null, null);
            if (value != newValue) {
                executeForLong("PRAGMA wal_autocheckpoint=" + newValue, null, null);
            }
        }
    
private voidsetForeignKeyModeFromConfiguration()

        if (!mIsReadOnlyConnection) {
            final long newValue = mConfiguration.foreignKeyConstraintsEnabled ? 1 : 0;
            long value = executeForLong("PRAGMA foreign_keys", null, null);
            if (value != newValue) {
                execute("PRAGMA foreign_keys=" + newValue, null, null);
            }
        }
    
private voidsetJournalMode(java.lang.String newValue)

        String value = executeForString("PRAGMA journal_mode", null, null);
        if (!value.equalsIgnoreCase(newValue)) {
            try {
                String result = executeForString("PRAGMA journal_mode=" + newValue, null, null);
                if (result.equalsIgnoreCase(newValue)) {
                    return;
                }
                // PRAGMA journal_mode silently fails and returns the original journal
                // mode in some cases if the journal mode could not be changed.
            } catch (SQLiteDatabaseLockedException ex) {
                // This error (SQLITE_BUSY) occurs if one connection has the database
                // open in WAL mode and another tries to change it to non-WAL.
            }
            // Because we always disable WAL mode when a database is first opened
            // (even if we intend to re-enable it), we can encounter problems if
            // there is another open connection to the database somewhere.
            // This can happen for a variety of reasons such as an application opening
            // the same database in multiple processes at the same time or if there is a
            // crashing content provider service that the ActivityManager has
            // removed from its registry but whose process hasn't quite died yet
            // by the time it is restarted in a new process.
            //
            // If we don't change the journal mode, nothing really bad happens.
            // In the worst case, an application that enables WAL might not actually
            // get it, although it can still use connection pooling.
            Log.w(TAG, "Could not change the database journal mode of '"
                    + mConfiguration.label + "' from '" + value + "' to '" + newValue
                    + "' because the database is locked.  This usually means that "
                    + "there are other open connections to the database which prevents "
                    + "the database from enabling or disabling write-ahead logging mode.  "
                    + "Proceeding without changing the journal mode.");
        }
    
private voidsetJournalSizeLimit()

        if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
            final long newValue = SQLiteGlobal.getJournalSizeLimit();
            long value = executeForLong("PRAGMA journal_size_limit", null, null);
            if (value != newValue) {
                executeForLong("PRAGMA journal_size_limit=" + newValue, null, null);
            }
        }
    
private voidsetLocaleFromConfiguration()

        if ((mConfiguration.openFlags & SQLiteDatabase.NO_LOCALIZED_COLLATORS) != 0) {
            return;
        }

        // Register the localized collators.
        final String newLocale = mConfiguration.locale.toString();
        nativeRegisterLocalizedCollators(mConnectionPtr, newLocale);

        // If the database is read-only, we cannot modify the android metadata table
        // or existing indexes.
        if (mIsReadOnlyConnection) {
            return;
        }

        try {
            // Ensure the android metadata table exists.
            execute("CREATE TABLE IF NOT EXISTS android_metadata (locale TEXT)", null, null);

            // Check whether the locale was actually changed.
            final String oldLocale = executeForString("SELECT locale FROM android_metadata "
                    + "UNION SELECT NULL ORDER BY locale DESC LIMIT 1", null, null);
            if (oldLocale != null && oldLocale.equals(newLocale)) {
                return;
            }

            // Go ahead and update the indexes using the new locale.
            execute("BEGIN", null, null);
            boolean success = false;
            try {
                execute("DELETE FROM android_metadata", null, null);
                execute("INSERT INTO android_metadata (locale) VALUES(?)",
                        new Object[] { newLocale }, null);
                execute("REINDEX LOCALIZED", null, null);
                success = true;
            } finally {
                execute(success ? "COMMIT" : "ROLLBACK", null, null);
            }
        } catch (RuntimeException ex) {
            throw new SQLiteException("Failed to change locale for db '" + mConfiguration.label
                    + "' to '" + newLocale + "'.", ex);
        }
    
voidsetOnlyAllowReadOnlyOperations(boolean readOnly)

        mOnlyAllowReadOnlyOperations = readOnly;
    
private voidsetPageSize()

        if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
            final long newValue = SQLiteGlobal.getDefaultPageSize();
            long value = executeForLong("PRAGMA page_size", null, null);
            if (value != newValue) {
                execute("PRAGMA page_size=" + newValue, null, null);
            }
        }
    
private voidsetSyncMode(java.lang.String newValue)

        String value = executeForString("PRAGMA synchronous", null, null);
        if (!canonicalizeSyncMode(value).equalsIgnoreCase(
                canonicalizeSyncMode(newValue))) {
            execute("PRAGMA synchronous=" + newValue, null, null);
        }
    
private voidsetWalModeFromConfiguration()

        if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
            if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
                setJournalMode("WAL");
                setSyncMode(SQLiteGlobal.getWALSyncMode());
            } else {
                setJournalMode(SQLiteGlobal.getDefaultJournalMode());
                setSyncMode(SQLiteGlobal.getDefaultSyncMode());
            }
        }
    
private voidthrowIfStatementForbidden(android.database.sqlite.SQLiteConnection$PreparedStatement statement)

        if (mOnlyAllowReadOnlyOperations && !statement.mReadOnly) {
            throw new SQLiteException("Cannot execute this statement because it "
                    + "might modify the database but the connection is read-only.");
        }
    
public java.lang.StringtoString()

        return "SQLiteConnection: " + mConfiguration.path + " (" + mConnectionId + ")";
    
private static java.lang.StringtrimSqlForDisplay(java.lang.String sql)

        return TRIM_SQL_PATTERN.matcher(sql).replaceAll(" ");