Home | History | Annotate | Download | only in sqlite
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.database.sqlite;
     18 
     19 import android.database.CursorWindow;
     20 import android.os.SystemClock;
     21 import android.util.Log;
     22 
     23 /**
     24  * A SQLite program that represents a query that reads the resulting rows into a CursorWindow.
     25  * This class is used by SQLiteCursor and isn't useful itself.
     26  *
     27  * SQLiteQuery is not internally synchronized so code using a SQLiteQuery from multiple
     28  * threads should perform its own synchronization when using the SQLiteQuery.
     29  */
     30 public class SQLiteQuery extends SQLiteProgram {
     31     private static final String TAG = "Cursor";
     32 
     33     /** The index of the unbound OFFSET parameter */
     34     private int mOffsetIndex;
     35 
     36     /** Args to bind on requery */
     37     private String[] mBindArgs;
     38 
     39     private boolean mClosed = false;
     40 
     41     /**
     42      * Create a persistent query object.
     43      *
     44      * @param db The database that this query object is associated with
     45      * @param query The SQL string for this query.
     46      * @param offsetIndex The 1-based index to the OFFSET parameter,
     47      */
     48     /* package */ SQLiteQuery(SQLiteDatabase db, String query, int offsetIndex, String[] bindArgs) {
     49         super(db, query);
     50 
     51         mOffsetIndex = offsetIndex;
     52         mBindArgs = bindArgs;
     53     }
     54 
     55     /**
     56      * Reads rows into a buffer. This method acquires the database lock.
     57      *
     58      * @param window The window to fill into
     59      * @return number of total rows in the query
     60      */
     61     /* package */ int fillWindow(CursorWindow window,
     62             int maxRead, int lastPos) {
     63         long timeStart = SystemClock.uptimeMillis();
     64         mDatabase.lock();
     65         mDatabase.logTimeStat(mSql, timeStart, SQLiteDatabase.GET_LOCK_LOG_PREFIX);
     66         try {
     67             acquireReference();
     68             try {
     69                 window.acquireReference();
     70                 // if the start pos is not equal to 0, then most likely window is
     71                 // too small for the data set, loading by another thread
     72                 // is not safe in this situation. the native code will ignore maxRead
     73                 int numRows = native_fill_window(window, window.getStartPosition(), mOffsetIndex,
     74                         maxRead, lastPos);
     75 
     76                 // Logging
     77                 if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
     78                     Log.d(TAG, "fillWindow(): " + mSql);
     79                 }
     80                 mDatabase.logTimeStat(mSql, timeStart);
     81                 return numRows;
     82             } catch (IllegalStateException e){
     83                 // simply ignore it
     84                 return 0;
     85             } catch (SQLiteDatabaseCorruptException e) {
     86                 mDatabase.onCorruption();
     87                 throw e;
     88             } finally {
     89                 window.releaseReference();
     90             }
     91         } finally {
     92             releaseReference();
     93             mDatabase.unlock();
     94         }
     95     }
     96 
     97     /**
     98      * Get the column count for the statement. Only valid on query based
     99      * statements. The database must be locked
    100      * when calling this method.
    101      *
    102      * @return The number of column in the statement's result set.
    103      */
    104     /* package */ int columnCountLocked() {
    105         acquireReference();
    106         try {
    107             return native_column_count();
    108         } finally {
    109             releaseReference();
    110         }
    111     }
    112 
    113     /**
    114      * Retrieves the column name for the given column index. The database must be locked
    115      * when calling this method.
    116      *
    117      * @param columnIndex the index of the column to get the name for
    118      * @return The requested column's name
    119      */
    120     /* package */ String columnNameLocked(int columnIndex) {
    121         acquireReference();
    122         try {
    123             return native_column_name(columnIndex);
    124         } finally {
    125             releaseReference();
    126         }
    127     }
    128 
    129     @Override
    130     public String toString() {
    131         return "SQLiteQuery: " + mSql;
    132     }
    133 
    134     @Override
    135     public void close() {
    136         super.close();
    137         mClosed = true;
    138     }
    139 
    140     /**
    141      * Called by SQLiteCursor when it is requeried.
    142      */
    143     /* package */ void requery() {
    144         if (mBindArgs != null) {
    145             int len = mBindArgs.length;
    146             try {
    147                 for (int i = 0; i < len; i++) {
    148                     super.bindString(i + 1, mBindArgs[i]);
    149                 }
    150             } catch (SQLiteMisuseException e) {
    151                 StringBuilder errMsg = new StringBuilder("mSql " + mSql);
    152                 for (int i = 0; i < len; i++) {
    153                     errMsg.append(" ");
    154                     errMsg.append(mBindArgs[i]);
    155                 }
    156                 errMsg.append(" ");
    157                 IllegalStateException leakProgram = new IllegalStateException(
    158                         errMsg.toString(), e);
    159                 throw leakProgram;
    160             }
    161         }
    162     }
    163 
    164     @Override
    165     public void bindNull(int index) {
    166         mBindArgs[index - 1] = null;
    167         if (!mClosed) super.bindNull(index);
    168     }
    169 
    170     @Override
    171     public void bindLong(int index, long value) {
    172         mBindArgs[index - 1] = Long.toString(value);
    173         if (!mClosed) super.bindLong(index, value);
    174     }
    175 
    176     @Override
    177     public void bindDouble(int index, double value) {
    178         mBindArgs[index - 1] = Double.toString(value);
    179         if (!mClosed) super.bindDouble(index, value);
    180     }
    181 
    182     @Override
    183     public void bindString(int index, String value) {
    184         mBindArgs[index - 1] = value;
    185         if (!mClosed) super.bindString(index, value);
    186     }
    187 
    188     private final native int native_fill_window(CursorWindow window,
    189             int startPos, int offsetParam, int maxRead, int lastPos);
    190 
    191     private final native int native_column_count();
    192 
    193     private final native String native_column_name(int columnIndex);
    194 }
    195